From 1358efe896c2f1279a29189c185738f6bcb0822c Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 6 Jul 2020 14:09:33 +0530 Subject: [PATCH 001/728] feat: update icons --- .../desk_page/accounting/accounting.json | 3 +- .../desk_page/agriculture/agriculture.json | 4 +- erpnext/assets/desk_page/assets/assets.json | 3 +- erpnext/buying/desk_page/buying/buying.json | 9 ++-- erpnext/crm/desk_page/crm/crm.json | 7 +-- .../desk_page/education/education.json | 4 +- erpnext/hr/desk_page/hr/hr.json | 43 +++++++++++++------ .../loan_management/desk_page/loan/loan.json | 6 ++- .../manufacturing/manufacturing.json | 11 ++--- .../desk_page/non_profit/non_profit.json | 4 +- .../payroll/desk_page/payroll/payroll.json | 3 +- .../projects/desk_page/projects/projects.json | 7 +-- .../desk_page/quality/quality.json | 5 ++- erpnext/selling/desk_page/retail/retail.json | 4 +- .../selling/desk_page/selling/selling.json | 9 ++-- .../erpnext_settings/erpnext_settings.json | 4 +- erpnext/setup/desk_page/home/home.json | 4 +- erpnext/stock/desk_page/stock/stock.json | 11 ++--- .../support/desk_page/support/support.json | 5 ++- 19 files changed, 94 insertions(+), 52 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 31315e4c71..46e426784d 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -95,10 +95,11 @@ "doctype": "Desk Page", "extends_another_page": 0, "hide_custom": 0, + "icon": "accounting", "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-06-19 12:42:44.054598", + "modified": "2020-06-30 18:33:06.526514", "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 e0d2c9ca25..094e1652b3 100644 --- a/erpnext/agriculture/desk_page/agriculture/agriculture.json +++ b/erpnext/agriculture/desk_page/agriculture/agriculture.json @@ -24,10 +24,12 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, + "icon": "agriculture", "idx": 0, "is_standard": 1, "label": "Agriculture", - "modified": "2020-04-01 11:28:51.032822", + "modified": "2020-06-30 18:35:25.350213", "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 449a5facb0..515fc22f05 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -30,10 +30,11 @@ "doctype": "Desk Page", "extends_another_page": 0, "hide_custom": 0, + "icon": "assets", "idx": 0, "is_standard": 1, "label": "Assets", - "modified": "2020-05-20 18:05:23.994795", + "modified": "2020-06-30 18:36:11.169586", "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 565d39c3c8..af95b931ad 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -57,10 +57,11 @@ "doctype": "Desk Page", "extends_another_page": 0, "hide_custom": 0, + "icon": "buying", "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-06-29 19:30:24.983050", + "modified": "2020-06-30 18:36:53.390498", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -70,7 +71,7 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#cef6d1", + "color": "Green", "format": "{} Available", "label": "Item", "link_to": "Item", @@ -78,7 +79,7 @@ "type": "DocType" }, { - "color": "#ffe8cd", + "color": "Yellow", "format": "{} Pending", "label": "Material Request", "link_to": "Material Request", @@ -86,7 +87,7 @@ "type": "DocType" }, { - "color": "#ffe8cd", + "color": "Yellow", "format": "{} To Receive", "label": "Purchase Order", "link_to": "Purchase Order", diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index eb69dc06b6..234008d38f 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -39,10 +39,11 @@ "doctype": "Desk Page", "extends_another_page": 0, "hide_custom": 0, + "icon": "crm", "idx": 0, "is_standard": 1, "label": "CRM", - "modified": "2020-05-28 13:33:52.906750", + "modified": "2020-06-30 18:37:48.961922", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -52,7 +53,7 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#ffe8cd", + "color": "Blue", "format": "{} Open", "label": "Lead", "link_to": "Lead", @@ -60,7 +61,7 @@ "type": "DocType" }, { - "color": "#cef6d1", + "color": "Blue", "format": "{} Assigned", "label": "Opportunity", "link_to": "Opportunity", diff --git a/erpnext/education/desk_page/education/education.json b/erpnext/education/desk_page/education/education.json index b341ec4b99..b8f87d34b7 100644 --- a/erpnext/education/desk_page/education/education.json +++ b/erpnext/education/desk_page/education/education.json @@ -79,10 +79,12 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, + "icon": "education", "idx": 0, "is_standard": 1, "label": "Education", - "modified": "2020-05-22 01:09:13.058482", + "modified": "2020-06-30 18:36:17.013044", "modified_by": "Administrator", "module": "Education", "name": "Education", diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 0fed8d322f..bcc4ac57c9 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -20,6 +20,11 @@ "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", @@ -45,6 +50,11 @@ "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", @@ -59,13 +69,18 @@ "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": [ { - "chart_name": "Attendance Count", - "label": "Attendance Count" + "chart_name": "Outgoing Salary", + "label": "Outgoing Salary" } ], "creation": "2020-03-02 15:48:58.322521", @@ -75,10 +90,11 @@ "doctype": "Desk Page", "extends_another_page": 0, "hide_custom": 0, + "icon": "hr", "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-06-16 19:20:50.976045", + "modified": "2020-06-30 18:34:39.966096", "modified_by": "Administrator", "module": "HR", "name": "HR", @@ -88,13 +104,21 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#9deca2", + "color": "Green", "format": "{} Active", "label": "Employee", "link_to": "Employee", "stats_filter": "{\"status\":\"Active\"}", "type": "DocType" }, + { + "color": "Yellow", + "format": "{} Open", + "label": "Leave Application", + "link_to": "Leave Application", + "stats_filter": "{\"status\":\"Open\"}", + "type": "DocType" + }, { "label": "Attendance", "link_to": "Attendance", @@ -102,15 +126,8 @@ "type": "DocType" }, { - "format": "{} Open", - "label": "Leave Application", - "link_to": "Leave Application", - "stats_filter": "{\"status\":\"Open\"}", - "type": "DocType" - }, - { - "label": "Job Applicant", - "link_to": "Job Applicant", + "label": "Salary Structure", + "link_to": "Salary Structure", "type": "DocType" }, { diff --git a/erpnext/loan_management/desk_page/loan/loan.json b/erpnext/loan_management/desk_page/loan/loan.json index 48193b0a0d..95fa22bd69 100644 --- a/erpnext/loan_management/desk_page/loan/loan.json +++ b/erpnext/loan_management/desk_page/loan/loan.json @@ -34,10 +34,12 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, + "icon": "loan", "idx": 0, "is_standard": 1, "label": "Loan", - "modified": "2020-06-07 19:42:14.947902", + "modified": "2020-06-30 18:33:53.854122", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", @@ -46,7 +48,7 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#ffe8cd", + "color": "Green", "format": "{} Open", "label": "Loan Application", "link_to": "Loan Application", diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index 8d11294164..3dd86a38bf 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -44,10 +44,11 @@ "doctype": "Desk Page", "extends_another_page": 0, "hide_custom": 0, + "icon": "organization", "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-05-28 13:54:02.048419", + "modified": "2020-06-30 18:40:04.454826", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -58,7 +59,7 @@ "restrict_to_domain": "Manufacturing", "shortcuts": [ { - "color": "#cef6d1", + "color": "Green", "format": "{} Active", "label": "Item", "link_to": "Item", @@ -67,7 +68,7 @@ "type": "DocType" }, { - "color": "#cef6d1", + "color": "Green", "format": "{} Active", "label": "BOM", "link_to": "BOM", @@ -76,7 +77,7 @@ "type": "DocType" }, { - "color": "#ffe8cd", + "color": "Yellow", "format": "{} Open", "label": "Work Order", "link_to": "Work Order", @@ -85,7 +86,7 @@ "type": "DocType" }, { - "color": "#ffe8cd", + "color": "Yellow", "format": "{} Open", "label": "Production Plan", "link_to": "Production Plan", 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 ebe6194893..24d655ad6f 100644 --- a/erpnext/non_profit/desk_page/non_profit/non_profit.json +++ b/erpnext/non_profit/desk_page/non_profit/non_profit.json @@ -39,10 +39,12 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, + "icon": "non-profit", "idx": 0, "is_standard": 1, "label": "Non Profit", - "modified": "2020-04-13 13:41:52.373705", + "modified": "2020-06-30 18:35:52.770917", "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 b5eac465c8..cb063c105c 100644 --- a/erpnext/payroll/desk_page/payroll/payroll.json +++ b/erpnext/payroll/desk_page/payroll/payroll.json @@ -35,10 +35,11 @@ "doctype": "Desk Page", "extends_another_page": 0, "hide_custom": 0, + "icon": "money-coins-1", "idx": 0, "is_standard": 1, "label": "Payroll", - "modified": "2020-06-19 12:23:06.034046", + "modified": "2020-06-30 18:33:29.072946", "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 e24cf3081c..3756c5bb50 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -30,10 +30,11 @@ "doctype": "Desk Page", "extends_another_page": 0, "hide_custom": 0, + "icon": "project", "idx": 0, "is_standard": 1, "label": "Projects", - "modified": "2020-05-28 13:38:19.934937", + "modified": "2020-06-30 18:38:40.130763", "modified_by": "Administrator", "module": "Projects", "name": "Projects", @@ -42,7 +43,7 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#cef6d1", + "color": "Blue", "format": "{} Assigned", "label": "Task", "link_to": "Task", @@ -50,7 +51,7 @@ "type": "DocType" }, { - "color": "#ffe8cd", + "color": "Blue", "format": "{} Open", "label": "Project", "link_to": "Project", diff --git a/erpnext/quality_management/desk_page/quality/quality.json b/erpnext/quality_management/desk_page/quality/quality.json index 5ee70008dd..7e5a966df2 100644 --- a/erpnext/quality_management/desk_page/quality/quality.json +++ b/erpnext/quality_management/desk_page/quality/quality.json @@ -29,11 +29,12 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", + "hide_custom": 0, + "icon": "quality", "idx": 0, "is_standard": 1, "label": "Quality", - "modified": "2020-04-01 11:28:51.095012", + "modified": "2020-06-30 18:35:36.017107", "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 7b30af20cc..ea62091ffd 100644 --- a/erpnext/selling/desk_page/retail/retail.json +++ b/erpnext/selling/desk_page/retail/retail.json @@ -14,10 +14,12 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, + "icon": "retail", "idx": 0, "is_standard": 1, "label": "Retail", - "modified": "2020-04-26 22:42:39.346750", + "modified": "2020-06-30 18:35:58.987449", "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 225238233a..7dcb71f5f0 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -41,10 +41,11 @@ "doctype": "Desk Page", "extends_another_page": 0, "hide_custom": 1, + "icon": "sell", "idx": 0, "is_standard": 1, "label": "Selling", - "modified": "2020-06-29 19:26:35.139097", + "modified": "2020-06-30 18:32:50.587478", "modified_by": "Administrator", "module": "Selling", "name": "Selling", @@ -54,7 +55,7 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#cef6d1", + "color": "Grey", "format": "{} Available", "label": "Item", "link_to": "Item", @@ -62,7 +63,7 @@ "type": "DocType" }, { - "color": "#ffe8cd", + "color": "Yellow", "format": "{} To Deliver", "label": "Sales Order", "link_to": "Sales Order", @@ -70,7 +71,7 @@ "type": "DocType" }, { - "color": "#cef6d1", + "color": "Grey", "format": "{} Open", "label": "Sales Analytics", "link_to": "Sales Analytics", diff --git a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json index 253d711b32..92b34b06ac 100644 --- a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json +++ b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json @@ -9,10 +9,12 @@ "doctype": "Desk Page", "extends": "Settings", "extends_another_page": 1, + "hide_custom": 0, + "icon": "settings", "idx": 0, "is_standard": 1, "label": "ERPNext Settings", - "modified": "2020-04-01 11:28:51.400851", + "modified": "2020-06-30 18:35:41.160158", "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 63cd5c5cec..19e6f62202 100644 --- a/erpnext/setup/desk_page/home/home.json +++ b/erpnext/setup/desk_page/home/home.json @@ -54,10 +54,12 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, + "icon": "getting-started", "idx": 0, "is_standard": 1, "label": "Home", - "modified": "2020-05-11 10:20:37.358701", + "modified": "2020-06-30 18:36:05.637904", "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 1bf81f7f0e..89f40f0142 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -55,10 +55,11 @@ "doctype": "Desk Page", "extends_another_page": 0, "hide_custom": 0, + "icon": "stock", "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-05-30 17:32:11.062681", + "modified": "2020-06-30 18:41:21.822817", "modified_by": "Administrator", "module": "Stock", "name": "Stock", @@ -68,7 +69,7 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#cef6d1", + "color": "Green", "format": "{} Available", "label": "Item", "link_to": "Item", @@ -76,7 +77,7 @@ "type": "DocType" }, { - "color": "#ffe8cd", + "color": "Yellow", "format": "{} Pending", "label": "Material Request", "link_to": "Material Request", @@ -89,7 +90,7 @@ "type": "DocType" }, { - "color": "#ffe8cd", + "color": "Yellow", "format": "{} To Bill", "label": "Purchase Receipt", "link_to": "Purchase Receipt", @@ -97,7 +98,7 @@ "type": "DocType" }, { - "color": "#ffe8cd", + "color": "Yellow", "format": "{} To Bill", "label": "Delivery Note", "link_to": "Delivery Note", diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json index b1ad7c8aa0..1a73e05ca3 100644 --- a/erpnext/support/desk_page/support/support.json +++ b/erpnext/support/desk_page/support/support.json @@ -40,10 +40,11 @@ "doctype": "Desk Page", "extends_another_page": 0, "hide_custom": 0, + "icon": "support", "idx": 0, "is_standard": 1, "label": "Support", - "modified": "2020-06-04 11:54:56.124219", + "modified": "2020-06-30 18:41:35.683929", "modified_by": "Administrator", "module": "Support", "name": "Support", @@ -52,7 +53,7 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#ffc4c4", + "color": "Yellow", "format": "{} Assigned", "label": "Issue", "link_to": "Issue", From be5e129259490586c8e7b3f94b222a0c5b87b93e Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 8 Jul 2020 12:56:28 +0530 Subject: [PATCH 002/728] feat: update icons for erpnext settings shortcuts --- .../erpnext_settings/erpnext_settings.json | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json index 92b34b06ac..5c8cd6977b 100644 --- a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json +++ b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json @@ -14,7 +14,7 @@ "idx": 0, "is_standard": 1, "label": "ERPNext Settings", - "modified": "2020-06-30 18:35:41.160158", + "modified": "2020-07-08 12:53:44.904241", "modified_by": "Administrator", "module": "Setup", "name": "ERPNext Settings", @@ -23,89 +23,89 @@ "pin_to_top": 0, "shortcuts": [ { - "icon": "octicon octicon-rocket", + "icon": "project", "label": "Projects Settings", "link_to": "Projects Settings", "type": "DocType" }, { - "icon": "octicon octicon-repo", + "icon": "accounting", "label": "Accounts Settings", "link_to": "Accounts Settings", "type": "DocType" }, { - "icon": "octicon octicon-package", + "icon": "stock", "label": "Stock Settings", "link_to": "Stock Settings", "type": "DocType" }, { - "icon": "octicon octicon-organization", + "icon": "hr", "label": "HR Settings", "link_to": "HR Settings", "type": "DocType" }, { - "icon": "octicon octicon-tag", + "icon": "sell", "label": "Selling Settings", "link_to": "Selling Settings", "type": "DocType" }, { - "icon": "octicon octicon-briefcase", + "icon": "buying", "label": "Buying Settings", "link_to": "Buying Settings", "type": "DocType" }, { - "icon": "fa fa-life-ring", + "icon": "support", "label": "Support Settings", "link_to": "Support Settings", "type": "DocType" }, { - "icon": "fa fa-shopping-cart", + "icon": "retail", "label": "Shopping Cart Settings", "link_to": "Shopping Cart Settings", "type": "DocType" }, { - "icon": "fa fa-globe", + "icon": "website", "label": "Portal Settings", "link_to": "Portal Settings", "type": "DocType" }, { - "icon": "octicon octicon-tools", + "icon": "organization", "label": "Manufacturing Settings", "link_to": "Manufacturing Settings", "restrict_to_domain": "Manufacturing", "type": "DocType" }, { - "icon": "octicon octicon-mortar-board", + "icon": "education", "label": "Education Settings", "link_to": "Education Settings", "restrict_to_domain": "Education", "type": "DocType" }, { - "icon": "fa fa-bed", + "icon": "organization", "label": "Hotel Settings", "link_to": "Hotel Settings", "restrict_to_domain": "Hospitality", "type": "DocType" }, { - "icon": "fa fa-heartbeat", + "icon": "non-profit", "label": "Healthcare Settings", "link_to": "Healthcare Settings", "restrict_to_domain": "Healthcare", "type": "DocType" }, { - "icon": "fa fa-cog", + "icon": "setting", "label": "Domain Settings", "link_to": "Domain Settings", "type": "DocType" From 840550e25feac445f213a0cd5d7d6f7d5a5b07a5 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 8 Jul 2020 15:53:35 +0530 Subject: [PATCH 003/728] refactor: change sequence for profit and loss statement --- .../profit_and_loss_statement.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index 7caa7648b1..37af560716 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -59,13 +59,6 @@ def get_report_summary(period_list, periodicity, income, expense, net_profit_los expense_label = _("Total Expense") return [ - { - "value": net_profit, - "indicator": "Green" if net_profit > 0 else "Red", - "label": profit_label, - "datatype": "Currency", - "currency": net_profit_loss.get("currency") if net_profit_loss else default_currency - }, { "value": net_income, "label": income_label, @@ -77,6 +70,13 @@ def get_report_summary(period_list, periodicity, income, expense, net_profit_los "label": expense_label, "datatype": "Currency", "currency": expense[-1].get('currency') if expense else default_currency + }, + { + "value": net_profit, + "indicator": "Green" if net_profit > 0 else "Red", + "label": profit_label, + "datatype": "Currency", + "currency": net_profit_loss.get("currency") if net_profit_loss else default_currency } ] From f59d927f73a44dcb683659fd54d757391e752823 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 9 Jul 2020 10:58:05 +0530 Subject: [PATCH 004/728] feat: update summary items --- erpnext/accounts/report/balance_sheet/balance_sheet.py | 3 --- .../profit_and_loss_statement/profit_and_loss_statement.py | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index a858c1998f..1729abce9e 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -147,7 +147,6 @@ def get_report_summary(period_list, asset, liability, equity, provisional_profit { "value": net_asset, "label": "Total Asset", - "indicator": "Green", "datatype": "Currency", "currency": currency }, @@ -155,14 +154,12 @@ def get_report_summary(period_list, asset, liability, equity, provisional_profit "value": net_liability, "label": "Total Liability", "datatype": "Currency", - "indicator": "Red", "currency": currency }, { "value": net_equity, "label": "Total Equity", "datatype": "Currency", - "indicator": "Blue", "currency": currency }, { diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index 37af560716..01e1b3ffc3 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -65,12 +65,14 @@ def get_report_summary(period_list, periodicity, income, expense, net_profit_los "datatype": "Currency", "currency": income[-1].get('currency') if income else default_currency }, + { "type": "separator", "value": "-"}, { "value": net_expense, "label": expense_label, "datatype": "Currency", "currency": expense[-1].get('currency') if expense else default_currency }, + { "type": "separator", "value": "=", "color": "blue"}, { "value": net_profit, "indicator": "Green" if net_profit > 0 else "Red", From b6fb13258fec75b963656d8cae77cb78ac4a4eb4 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 9 Jul 2020 16:51:36 +0530 Subject: [PATCH 005/728] feat: move financial statement switcher to page toolbar --- erpnext/public/js/financial_statements.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 459c01b269..b2f7afe53f 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -57,18 +57,22 @@ erpnext.financial_statements = { }); }); - report.page.add_inner_button(__("Balance Sheet"), function() { + const views_menu = report.page.add_custom_button_group(__('Financial Statements')); + + report.page.add_custom_menu_item(views_menu, __("Balance Sheet"), function() { var filters = report.get_values(); frappe.set_route('query-report', 'Balance Sheet', {company: filters.company}); - }, __('Financial Statements')); - report.page.add_inner_button(__("Profit and Loss"), function() { + }); + + report.page.add_custom_menu_item(views_menu, __("Profit and Loss"), function() { var filters = report.get_values(); frappe.set_route('query-report', 'Profit and Loss Statement', {company: filters.company}); - }, __('Financial Statements')); - report.page.add_inner_button(__("Cash Flow Statement"), function() { + }); + + report.page.add_custom_menu_item(views_menu, __("Cash Flow Statement"), function() { var filters = report.get_values(); frappe.set_route('query-report', 'Cash Flow', {company: filters.company}); - }, __('Financial Statements')); + }); } }; From 66069df02064ee19af9b8dc429b56f1a86d01956 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 31 Jul 2020 15:54:05 +0530 Subject: [PATCH 006/728] 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 007/728] 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 008/728] 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 9d829532420689bb2d4692b77180f3c47c54e3b5 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Thu, 6 Aug 2020 23:58:56 +0530 Subject: [PATCH 009/728] fix: Opportunity Status fix --- erpnext/selling/doctype/quotation/quotation.py | 17 ++++++++--------- .../selling/doctype/sales_order/sales_order.py | 1 - 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 449a968a4f..01479a1654 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -25,7 +25,6 @@ class Quotation(SellingController): def validate(self): super(Quotation, self).validate() self.set_status() - self.update_opportunity() self.validate_uom_is_integer("stock_uom", "qty") self.validate_valid_till() self.set_customer_name() @@ -50,20 +49,20 @@ class Quotation(SellingController): lead_name, company_name = frappe.db.get_value("Lead", self.party_name, ["lead_name", "company_name"]) self.customer_name = company_name or lead_name - def update_opportunity(self): + def update_opportunity(self, status): for opportunity in list(set([d.prevdoc_docname for d in self.get("items")])): if opportunity: - self.update_opportunity_status(opportunity) + self.update_opportunity_status(status, opportunity) if self.opportunity: - self.update_opportunity_status() + self.update_opportunity_status(status) - def update_opportunity_status(self, opportunity=None): + def update_opportunity_status(self, status, opportunity=None): if not opportunity: opportunity = self.opportunity opp = frappe.get_doc("Opportunity", opportunity) - opp.status = None + opp.status = status opp.set_status(update=True) def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None): @@ -82,7 +81,7 @@ class Quotation(SellingController): else: frappe.throw(_("Invalid lost reason {0}, please create a new lost reason").format(frappe.bold(reason.get('lost_reason')))) - self.update_opportunity() + self.update_opportunity('Lost') self.update_lead() self.save() @@ -95,7 +94,7 @@ class Quotation(SellingController): self.company, self.base_grand_total, self) #update enquiry status - self.update_opportunity() + self.update_opportunity('Quotation') self.update_lead() def on_cancel(self): @@ -105,7 +104,7 @@ class Quotation(SellingController): #update enquiry status self.set_status(update=True) - self.update_opportunity() + self.update_opportunity('Open') self.update_lead() def print_other_charges(self,docname): diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index ffb66354fa..f17af69e5b 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -159,7 +159,6 @@ class SalesOrder(SellingController): frappe.throw(_("Quotation {0} is cancelled").format(quotation)) doc.set_status(update=True) - doc.update_opportunity() def validate_drop_ship(self): for d in self.get('items'): From ecc7eedd101f03bbfeb47b78bbdf46e75e28b02c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sun, 23 Aug 2020 16:35:39 +0530 Subject: [PATCH 010/728] fix: Update erpnext logo --- erpnext/hooks.py | 2 +- erpnext/public/images/erpnext_logo.svg | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 erpnext/public/images/erpnext_logo.svg diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 463ad6c94b..1c36c2a30b 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -10,7 +10,7 @@ app_color = "#e74c3c" app_email = "info@erpnext.com" app_license = "GNU General Public License (v3)" source_link = "https://github.com/frappe/erpnext" -app_logo_url = '/assets/erpnext/images/erp-icon.svg' +app_logo_url = '/assets/erpnext/images/erpnext_logo.svg' develop_version = '13.x.x-develop' diff --git a/erpnext/public/images/erpnext_logo.svg b/erpnext/public/images/erpnext_logo.svg new file mode 100644 index 0000000000..af3a84953b --- /dev/null +++ b/erpnext/public/images/erpnext_logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From a43e760703afc80d5b37f51fe881379b1cdaee3a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sun, 23 Aug 2020 16:36:28 +0530 Subject: [PATCH 011/728] fix: Item dashboard style --- erpnext/public/less/erpnext.less | 5 +++-- erpnext/stock/dashboard/item_dashboard_list.html | 8 ++++---- erpnext/stock/doctype/item/item_dashboard.py | 8 ++++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/erpnext/public/less/erpnext.less b/erpnext/public/less/erpnext.less index 8685837d33..1488bdd9c7 100644 --- a/erpnext/public/less/erpnext.less +++ b/erpnext/public/less/erpnext.less @@ -39,8 +39,9 @@ .dashboard-list-item { background-color: inherit; - padding: 5px 0px; - border-bottom: 1px solid @border-color; + border-bottom: 1px solid var(--border-color); + font-size: var(--text-md); + color: var(--text-color); } #page-stock-balance .dashboard-list-item { diff --git a/erpnext/stock/dashboard/item_dashboard_list.html b/erpnext/stock/dashboard/item_dashboard_list.html index e1914ed76a..0c10be462a 100644 --- a/erpnext/stock/dashboard/item_dashboard_list.html +++ b/erpnext/stock/dashboard/item_dashboard_list.html @@ -1,10 +1,10 @@ {% for d in data %}
-
+ -
+
{% if show_item %} {{ d.item_code }} @@ -12,7 +12,7 @@ {% endif %}
-
+
{{ d.total_reserved }} @@ -40,7 +40,7 @@
{% if can_write %} -
+
{% if d.actual_qty %}
diff --git a/erpnext/education/doctype/student_admission/templates/student_admission_row.html b/erpnext/education/doctype/student_admission/templates/student_admission_row.html index e049773037..726602d21c 100644 --- a/erpnext/education/doctype/student_admission/templates/student_admission_row.html +++ b/erpnext/education/doctype/student_admission/templates/student_admission_row.html @@ -11,7 +11,7 @@ {% elif frappe.utils.getdate(doc.admission_start_date) > today %} blue {% else %} - darkgrey + darkgray {% endif %} ">{{ doc.title }}
diff --git a/erpnext/hr/doctype/employee/employee_list.js b/erpnext/hr/doctype/employee/employee_list.js index 7a66d12bf5..1a82dd8be7 100644 --- a/erpnext/hr/doctype/employee/employee_list.js +++ b/erpnext/hr/doctype/employee/employee_list.js @@ -3,7 +3,7 @@ frappe.listview_settings['Employee'] = { filters: [["status","=", "Active"]], get_indicator: function(doc) { var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status]; - indicator[1] = {"Active": "green", "Temporary Leave": "red", "Left": "darkgrey"}[doc.status]; + indicator[1] = {"Active": "green", "Temporary Leave": "red", "Left": "darkgray"}[doc.status]; return indicator; } }; diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js b/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js index 93f7b8356b..1af05bde69 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js @@ -5,7 +5,7 @@ frappe.listview_settings['Leave Allocation'] = { get_indicator: function(doc) { if(doc.status==="Expired") { - return [__("Expired"), "darkgrey", "expired, =, 1"]; + return [__("Expired"), "darkgray", "expired, =, 1"]; } }, }; diff --git a/erpnext/hr/page/team_updates/team_updates.js b/erpnext/hr/page/team_updates/team_updates.js index da1f5316a0..030fa66549 100644 --- a/erpnext/hr/page/team_updates/team_updates.js +++ b/erpnext/hr/page/team_updates/team_updates.js @@ -41,7 +41,7 @@ frappe.team_updates = { me.add_row(d); }); } else { - frappe.show_alert({message:__('No more updates'), indicator:'darkgrey'}); + frappe.show_alert({message:__('No more updates'), indicator:'darkgray'}); me.more.parent().addClass('hidden'); } } diff --git a/erpnext/manufacturing/doctype/bom/bom_list.js b/erpnext/manufacturing/doctype/bom/bom_list.js index 94cb466bd8..90d1d82528 100644 --- a/erpnext/manufacturing/doctype/bom/bom_list.js +++ b/erpnext/manufacturing/doctype/bom/bom_list.js @@ -8,7 +8,7 @@ frappe.listview_settings['BOM'] = { } else if(doc.is_active) { return [__("Active"), "blue", "is_active,=,Yes"]; } else if(!doc.is_active) { - return [__("Not active"), "darkgrey", "is_active,=,No"]; + return [__("Not active"), "darkgray", "is_active,=,No"]; } } }; diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js index d377ef0af7..f5a491402a 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js @@ -9,8 +9,8 @@ frappe.listview_settings['Production Plan'] = { "Draft": "red", "In Process": "orange", "Completed": "green", - "Material Requested": "darkgrey", - "Cancelled": "darkgrey" + "Material Requested": "darkgray", + "Cancelled": "darkgray" }[doc.status], "status,=," + doc.status]; } } diff --git a/erpnext/manufacturing/doctype/work_order/work_order_list.js b/erpnext/manufacturing/doctype/work_order/work_order_list.js index 8d18395acd..6959a347c4 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order_list.js +++ b/erpnext/manufacturing/doctype/work_order/work_order_list.js @@ -12,7 +12,7 @@ frappe.listview_settings['Work Order'] = { "Not Started": "red", "In Process": "orange", "Completed": "green", - "Cancelled": "darkgrey" + "Cancelled": "darkgray" }[doc.status], "status,=," + doc.status]; } } diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index ab095ebfe0..af34fe8a04 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -19,7 +19,7 @@ class Quotation(SellingController): self.indicator_color = 'blue' self.indicator_title = 'Submitted' if self.valid_till and getdate(self.valid_till) < getdate(nowdate()): - self.indicator_color = 'darkgrey' + self.indicator_color = 'darkgray' self.indicator_title = 'Expired' def validate(self): diff --git a/erpnext/selling/doctype/quotation/quotation_list.js b/erpnext/selling/doctype/quotation/quotation_list.js index f425acf180..41a9397e54 100644 --- a/erpnext/selling/doctype/quotation/quotation_list.js +++ b/erpnext/selling/doctype/quotation/quotation_list.js @@ -20,9 +20,9 @@ frappe.listview_settings['Quotation'] = { } else if(doc.status==="Ordered") { return [__("Ordered"), "green", "status,=,Ordered"]; } else if(doc.status==="Lost") { - return [__("Lost"), "darkgrey", "status,=,Lost"]; + return [__("Lost"), "darkgray", "status,=,Lost"]; } else if(doc.status==="Expired") { - return [__("Expired"), "darkgrey", "status,=,Expired"]; + return [__("Expired"), "darkgray", "status,=,Expired"]; } } }; diff --git a/erpnext/stock/doctype/batch/batch_list.js b/erpnext/stock/doctype/batch/batch_list.js index d4f74c3a21..f4ab50c312 100644 --- a/erpnext/stock/doctype/batch/batch_list.js +++ b/erpnext/stock/doctype/batch/batch_list.js @@ -2,9 +2,9 @@ frappe.listview_settings['Batch'] = { add_fields: ["item", "expiry_date", "batch_qty", "disabled"], get_indicator: (doc) => { if (doc.disabled) { - return [__("Disabled"), "darkgrey", "disabled,=,1"]; + return [__("Disabled"), "darkgray", "disabled,=,1"]; } else if (!doc.batch_qty) { - return [__("Empty"), "darkgrey", "batch_qty,=,0|disabled,=,0"]; + return [__("Empty"), "darkgray", "batch_qty,=,0|disabled,=,0"]; } else if (doc.expiry_date && frappe.datetime.get_diff(doc.expiry_date, frappe.datetime.nowdate()) <= 0) { return [__("Expired"), "red", "expiry_date,not in,|expiry_date,<=,Today|batch_qty,>,0|disabled,=,0"] } else { diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_list.js b/erpnext/stock/doctype/delivery_note/delivery_note_list.js index 0ae7c37b3f..1fd12f187a 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note_list.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note_list.js @@ -3,7 +3,7 @@ frappe.listview_settings['Delivery Note'] = { "transporter_name", "grand_total", "is_return", "status", "currency"], get_indicator: function(doc) { if(cint(doc.is_return)==1) { - return [__("Return"), "darkgrey", "is_return,=,Yes"]; + return [__("Return"), "darkgray", "is_return,=,Yes"]; } else if (doc.status === "Closed") { return [__("Closed"), "green", "status,=,Closed"]; } else if (flt(doc.per_billed, 2) < 100) { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js index e81f323a46..f294ce9fcc 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js @@ -3,7 +3,7 @@ frappe.listview_settings['Purchase Receipt'] = { "transporter_name", "is_return", "status", "per_billed", "currency"], get_indicator: function(doc) { if(cint(doc.is_return)==1) { - return [__("Return"), "darkgrey", "is_return,=,Yes"]; + return [__("Return"), "darkgray", "is_return,=,Yes"]; } else if (doc.status === "Closed") { return [__("Closed"), "green", "status,=,Closed"]; } else if (flt(doc.grand_total) !== 0 && flt(doc.per_billed, 2) < 100) { diff --git a/erpnext/support/doctype/issue/issue_list.js b/erpnext/support/doctype/issue/issue_list.js index 513a8dca22..2a8f05dc42 100644 --- a/erpnext/support/doctype/issue/issue_list.js +++ b/erpnext/support/doctype/issue/issue_list.js @@ -28,7 +28,7 @@ frappe.listview_settings['Issue'] = { } else if (doc.status === 'Closed') { return [__(doc.status), "green", "status,=," + doc.status]; } else { - return [__(doc.status), "darkgrey", "status,=," + doc.status]; + return [__(doc.status), "darkgray", "status,=," + doc.status]; } } } diff --git a/erpnext/templates/includes/address_row.html b/erpnext/templates/includes/address_row.html index dadd2dff10..01aea9100a 100644 --- a/erpnext/templates/includes/address_row.html +++ b/erpnext/templates/includes/address_row.html @@ -2,7 +2,7 @@
- {{ doc.address_title }} + {{ doc.address_title }}
{{ _(doc.address_type) }}
{{ doc.city }}
diff --git a/erpnext/templates/includes/issue_row.html b/erpnext/templates/includes/issue_row.html index ff868fabb5..e41e78abc6 100644 --- a/erpnext/templates/includes/issue_row.html +++ b/erpnext/templates/includes/issue_row.html @@ -2,7 +2,7 @@
- {% set indicator = 'red' if doc.status == 'Open' else 'darkgrey' %} + {% set indicator = 'red' if doc.status == 'Open' else 'darkgray' %} {% set indicator = 'green' if doc.status == 'Closed' else indicator %} {{ doc.name }} @@ -10,7 +10,7 @@
{{ doc.subject }}
- {% set indicator = 'red' if doc.status == 'Open' else 'darkgrey' %} + {% set indicator = 'red' if doc.status == 'Open' else 'darkgray' %} {% set indicator = 'green' if doc.status == 'Closed' else indicator %} {% set indicator = 'orange' if doc.status == 'Open' and doc.priority == 'Medium' else indicator %} {% set indicator = 'yellow' if doc.status == 'Open' and doc.priority == 'Low' else indicator %} diff --git a/erpnext/templates/includes/projects/project_row.html b/erpnext/templates/includes/projects/project_row.html index 73c83ef560..3fd102a737 100644 --- a/erpnext/templates/includes/projects/project_row.html +++ b/erpnext/templates/includes/projects/project_row.html @@ -15,7 +15,7 @@
{% else %} - + {{ doc.status }} {% endif %}
diff --git a/erpnext/templates/includes/projects/project_tasks.html b/erpnext/templates/includes/projects/project_tasks.html index 94c692cdd2..2f148dd58d 100644 --- a/erpnext/templates/includes/projects/project_tasks.html +++ b/erpnext/templates/includes/projects/project_tasks.html @@ -3,7 +3,7 @@
- {{ task.subject }} + {{ task.subject }}
{{ _("modified") }} {{ frappe.utils.pretty_date(task.modified) }} @@ -16,9 +16,9 @@ {% else %} - + - {% endif %} + {% endif %} {% endif %}
diff --git a/erpnext/templates/includes/timesheet/timesheet_row.html b/erpnext/templates/includes/timesheet/timesheet_row.html index 4852f59b5d..12bbbfb3ae 100644 --- a/erpnext/templates/includes/timesheet/timesheet_row.html +++ b/erpnext/templates/includes/timesheet/timesheet_row.html @@ -1,7 +1,7 @@
- + {{ doc.name }}
diff --git a/erpnext/templates/includes/transaction_row.html b/erpnext/templates/includes/transaction_row.html index 80a542f74b..1f5cb4e92d 100644 --- a/erpnext/templates/includes/transaction_row.html +++ b/erpnext/templates/includes/transaction_row.html @@ -1,7 +1,7 @@
- + {{ doc.name }}
diff --git a/erpnext/templates/pages/material_request_info.html b/erpnext/templates/pages/material_request_info.html index 9d189895b6..fce4a40498 100644 --- a/erpnext/templates/pages/material_request_info.html +++ b/erpnext/templates/pages/material_request_info.html @@ -20,7 +20,7 @@
- + {{ _(doc.get('indicator_title')) or _(doc.status) or _("Submitted") }}
diff --git a/erpnext/templates/pages/order.html b/erpnext/templates/pages/order.html index 9e3c58b45b..9941446685 100644 --- a/erpnext/templates/pages/order.html +++ b/erpnext/templates/pages/order.html @@ -34,7 +34,7 @@
- + {{ _(doc.get('indicator_title')) or _(doc.status) or _("Submitted") }}
diff --git a/erpnext/templates/pages/rfq.html b/erpnext/templates/pages/rfq.html index 5b27a94553..7e7e7f4266 100644 --- a/erpnext/templates/pages/rfq.html +++ b/erpnext/templates/pages/rfq.html @@ -77,13 +77,13 @@
- {{d.name}} + {{d.name}}
- {{d.status}} + {{d.status}}
- {{d.transaction_date}} + {{d.transaction_date}}
Link From b6ebcd7559b42caa99e06cc8bc1be2055c6fe82d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 27 Aug 2020 18:57:18 +0530 Subject: [PATCH 015/728] refactor: Replace darkgray indicator with gray indicator --- .../doctype/payment_request/payment_request_list.js | 2 +- .../doctype/purchase_invoice/purchase_invoice_list.js | 8 ++++---- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 ++-- .../accounts/doctype/sales_invoice/sales_invoice_list.js | 4 ++-- .../accounts/doctype/subscription/subscription_list.js | 2 +- .../doctype/supplier_quotation/supplier_quotation_list.js | 4 ++-- .../doctype/supplier_scorecard/supplier_scorecard_list.js | 2 +- erpnext/crm/doctype/contract/contract_list.js | 2 +- .../student_admission/templates/student_admission.html | 2 +- .../templates/student_admission_row.html | 2 +- erpnext/hr/doctype/employee/employee_list.js | 2 +- .../hr/doctype/leave_allocation/leave_allocation_list.js | 2 +- erpnext/hr/page/team_updates/team_updates.js | 2 +- erpnext/manufacturing/doctype/bom/bom_list.js | 2 +- .../doctype/production_plan/production_plan_list.js | 4 ++-- .../manufacturing/doctype/work_order/work_order_list.js | 2 +- erpnext/selling/doctype/quotation/quotation.py | 2 +- erpnext/selling/doctype/quotation/quotation_list.js | 4 ++-- erpnext/stock/doctype/batch/batch_list.js | 4 ++-- erpnext/stock/doctype/delivery_note/delivery_note_list.js | 2 +- .../doctype/purchase_receipt/purchase_receipt_list.js | 2 +- erpnext/support/doctype/issue/issue_list.js | 2 +- erpnext/templates/includes/address_row.html | 2 +- erpnext/templates/includes/issue_row.html | 4 ++-- erpnext/templates/includes/projects/project_row.html | 2 +- erpnext/templates/includes/projects/project_tasks.html | 2 +- .../templates/includes/projects/project_timesheets.html | 2 +- erpnext/templates/includes/timesheet/timesheet_row.html | 2 +- erpnext/templates/includes/transaction_row.html | 2 +- erpnext/templates/pages/material_request_info.html | 2 +- erpnext/templates/pages/order.html | 2 +- erpnext/templates/pages/rfq.html | 6 +++--- 32 files changed, 44 insertions(+), 44 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request_list.js b/erpnext/accounts/doctype/payment_request/payment_request_list.js index 4137dfa3dc..85d729cd61 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request_list.js +++ b/erpnext/accounts/doctype/payment_request/payment_request_list.js @@ -2,7 +2,7 @@ frappe.listview_settings['Payment Request'] = { add_fields: ["status"], get_indicator: function(doc) { if(doc.status == "Draft") { - return [__("Draft"), "darkgray", "status,=,Draft"]; + return [__("Draft"), "gray", "status,=,Draft"]; } if(doc.status == "Requested") { return [__("Requested"), "green", "status,=,Requested"]; diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js index 613d661e08..661559a605 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js @@ -7,19 +7,19 @@ frappe.listview_settings['Purchase Invoice'] = { "currency", "is_return", "release_date", "on_hold"], get_indicator: function(doc) { if( (flt(doc.outstanding_amount) <= 0) && doc.docstatus == 1 && doc.status == 'Debit Note Issued') { - return [__("Debit Note Issued"), "darkgray", "outstanding_amount,<=,0"]; + return [__("Debit Note Issued"), "gray", "outstanding_amount,<=,0"]; } else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) { if(cint(doc.on_hold) && !doc.release_date) { - return [__("On Hold"), "darkgray"]; + return [__("On Hold"), "gray"]; } else if(cint(doc.on_hold) && doc.release_date && frappe.datetime.get_diff(doc.release_date, frappe.datetime.nowdate()) > 0) { - return [__("Temporarily on Hold"), "darkgray"]; + return [__("Temporarily on Hold"), "gray"]; } 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)) { - return [__("Return"), "darkgray", "is_return,=,Yes"]; + return [__("Return"), "gray", "is_return,=,Yes"]; } 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.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 89cdf853e2..cd39438442 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -53,7 +53,7 @@ class SalesInvoice(SellingController): """Set indicator for portal""" if self.outstanding_amount < 0: self.indicator_title = _("Credit Note Issued") - self.indicator_color = "darkgray" + self.indicator_color = "gray" elif self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()): self.indicator_color = "orange" self.indicator_title = _("Unpaid") @@ -62,7 +62,7 @@ class SalesInvoice(SellingController): self.indicator_title = _("Overdue") elif cint(self.is_return) == 1: self.indicator_title = _("Return") - self.indicator_color = "darkgray" + self.indicator_color = "gray" else: self.indicator_color = "green" self.indicator_title = _("Paid") diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js index c85773a31b..5ac86d6f25 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js @@ -10,8 +10,8 @@ frappe.listview_settings['Sales Invoice'] = { "Draft": "grey", "Unpaid": "orange", "Paid": "green", - "Return": "darkgray", - "Credit Note Issued": "darkgray", + "Return": "gray", + "Credit Note Issued": "gray", "Unpaid and Discounted": "orange", "Overdue and Discounted": "red", "Overdue": "red" diff --git a/erpnext/accounts/doctype/subscription/subscription_list.js b/erpnext/accounts/doctype/subscription/subscription_list.js index 4670c6b6bb..c7325fb9f7 100644 --- a/erpnext/accounts/doctype/subscription/subscription_list.js +++ b/erpnext/accounts/doctype/subscription/subscription_list.js @@ -11,7 +11,7 @@ frappe.listview_settings['Subscription'] = { } else if(doc.status === 'Unpaid') { return [__("Unpaid"), "red"]; } else if(doc.status === 'Cancelled') { - return [__("Cancelled"), "darkgray"]; + return [__("Cancelled"), "gray"]; } } }; \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js index ede154f9c8..5ab6c980d0 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js @@ -4,9 +4,9 @@ frappe.listview_settings['Supplier Quotation'] = { if(doc.status==="Ordered") { return [__("Ordered"), "green", "status,=,Ordered"]; } else if(doc.status==="Rejected") { - return [__("Lost"), "darkgray", "status,=,Lost"]; + return [__("Lost"), "gray", "status,=,Lost"]; } else if(doc.status==="Expired") { - return [__("Expired"), "darkgray", "status,=,Expired"]; + return [__("Expired"), "gray", "status,=,Expired"]; } } }; diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js index aa1e5c37f4..dc5474e3b4 100644 --- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js +++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js @@ -10,7 +10,7 @@ frappe.listview_settings["Supplier Scorecard"] = { if (doc.indicator_color) { return [__(doc.status), doc.indicator_color.toLowerCase(), "status,=," + doc.status]; } else { - return [__("Unknown"), "darkgray", "status,=,''"]; + return [__("Unknown"), "gray", "status,=,''"]; } }, diff --git a/erpnext/crm/doctype/contract/contract_list.js b/erpnext/crm/doctype/contract/contract_list.js index ff580455f7..a09d123ed2 100644 --- a/erpnext/crm/doctype/contract/contract_list.js +++ b/erpnext/crm/doctype/contract/contract_list.js @@ -6,7 +6,7 @@ frappe.listview_settings['Contract'] = { } else if (doc.status == "Active") { return [__(doc.status), "green", "status,=," + doc.status]; } else if (doc.status == "Inactive") { - return [__(doc.status), "darkgray", "status,=," + doc.status]; + return [__(doc.status), "gray", "status,=," + doc.status]; } }, }; \ No newline at end of file diff --git a/erpnext/education/doctype/student_admission/templates/student_admission.html b/erpnext/education/doctype/student_admission/templates/student_admission.html index 6fc79b6054..5cbf36a293 100644 --- a/erpnext/education/doctype/student_admission/templates/student_admission.html +++ b/erpnext/education/doctype/student_admission/templates/student_admission.html @@ -21,7 +21,7 @@ {% elif frappe.utils.getdate(doc.admission_start_date) > today %} blue"> Application will open {% else %} - darkgray + gray {% endif %}
diff --git a/erpnext/education/doctype/student_admission/templates/student_admission_row.html b/erpnext/education/doctype/student_admission/templates/student_admission_row.html index 726602d21c..288e542ab1 100644 --- a/erpnext/education/doctype/student_admission/templates/student_admission_row.html +++ b/erpnext/education/doctype/student_admission/templates/student_admission_row.html @@ -11,7 +11,7 @@ {% elif frappe.utils.getdate(doc.admission_start_date) > today %} blue {% else %} - darkgray + gray {% endif %} ">{{ doc.title }}
diff --git a/erpnext/hr/doctype/employee/employee_list.js b/erpnext/hr/doctype/employee/employee_list.js index 1a82dd8be7..44837030be 100644 --- a/erpnext/hr/doctype/employee/employee_list.js +++ b/erpnext/hr/doctype/employee/employee_list.js @@ -3,7 +3,7 @@ frappe.listview_settings['Employee'] = { filters: [["status","=", "Active"]], get_indicator: function(doc) { var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status]; - indicator[1] = {"Active": "green", "Temporary Leave": "red", "Left": "darkgray"}[doc.status]; + indicator[1] = {"Active": "green", "Temporary Leave": "red", "Left": "gray"}[doc.status]; return indicator; } }; diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js b/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js index 1af05bde69..3ab176f809 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js @@ -5,7 +5,7 @@ frappe.listview_settings['Leave Allocation'] = { get_indicator: function(doc) { if(doc.status==="Expired") { - return [__("Expired"), "darkgray", "expired, =, 1"]; + return [__("Expired"), "gray", "expired, =, 1"]; } }, }; diff --git a/erpnext/hr/page/team_updates/team_updates.js b/erpnext/hr/page/team_updates/team_updates.js index 030fa66549..29780b8161 100644 --- a/erpnext/hr/page/team_updates/team_updates.js +++ b/erpnext/hr/page/team_updates/team_updates.js @@ -41,7 +41,7 @@ frappe.team_updates = { me.add_row(d); }); } else { - frappe.show_alert({message:__('No more updates'), indicator:'darkgray'}); + frappe.show_alert({message:__('No more updates'), indicator:'gray'}); me.more.parent().addClass('hidden'); } } diff --git a/erpnext/manufacturing/doctype/bom/bom_list.js b/erpnext/manufacturing/doctype/bom/bom_list.js index 90d1d82528..4b5887f180 100644 --- a/erpnext/manufacturing/doctype/bom/bom_list.js +++ b/erpnext/manufacturing/doctype/bom/bom_list.js @@ -8,7 +8,7 @@ frappe.listview_settings['BOM'] = { } else if(doc.is_active) { return [__("Active"), "blue", "is_active,=,Yes"]; } else if(!doc.is_active) { - return [__("Not active"), "darkgray", "is_active,=,No"]; + return [__("Not active"), "gray", "is_active,=,No"]; } } }; diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js index f5a491402a..5c81494215 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js @@ -9,8 +9,8 @@ frappe.listview_settings['Production Plan'] = { "Draft": "red", "In Process": "orange", "Completed": "green", - "Material Requested": "darkgray", - "Cancelled": "darkgray" + "Material Requested": "gray", + "Cancelled": "gray" }[doc.status], "status,=," + doc.status]; } } diff --git a/erpnext/manufacturing/doctype/work_order/work_order_list.js b/erpnext/manufacturing/doctype/work_order/work_order_list.js index 6959a347c4..81c23bb710 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order_list.js +++ b/erpnext/manufacturing/doctype/work_order/work_order_list.js @@ -12,7 +12,7 @@ frappe.listview_settings['Work Order'] = { "Not Started": "red", "In Process": "orange", "Completed": "green", - "Cancelled": "darkgray" + "Cancelled": "gray" }[doc.status], "status,=," + doc.status]; } } diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index af34fe8a04..4fb19f88b8 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -19,7 +19,7 @@ class Quotation(SellingController): self.indicator_color = 'blue' self.indicator_title = 'Submitted' if self.valid_till and getdate(self.valid_till) < getdate(nowdate()): - self.indicator_color = 'darkgray' + self.indicator_color = 'gray' self.indicator_title = 'Expired' def validate(self): diff --git a/erpnext/selling/doctype/quotation/quotation_list.js b/erpnext/selling/doctype/quotation/quotation_list.js index 41a9397e54..b631685bd1 100644 --- a/erpnext/selling/doctype/quotation/quotation_list.js +++ b/erpnext/selling/doctype/quotation/quotation_list.js @@ -20,9 +20,9 @@ frappe.listview_settings['Quotation'] = { } else if(doc.status==="Ordered") { return [__("Ordered"), "green", "status,=,Ordered"]; } else if(doc.status==="Lost") { - return [__("Lost"), "darkgray", "status,=,Lost"]; + return [__("Lost"), "gray", "status,=,Lost"]; } else if(doc.status==="Expired") { - return [__("Expired"), "darkgray", "status,=,Expired"]; + return [__("Expired"), "gray", "status,=,Expired"]; } } }; diff --git a/erpnext/stock/doctype/batch/batch_list.js b/erpnext/stock/doctype/batch/batch_list.js index f4ab50c312..0de9fd0150 100644 --- a/erpnext/stock/doctype/batch/batch_list.js +++ b/erpnext/stock/doctype/batch/batch_list.js @@ -2,9 +2,9 @@ frappe.listview_settings['Batch'] = { add_fields: ["item", "expiry_date", "batch_qty", "disabled"], get_indicator: (doc) => { if (doc.disabled) { - return [__("Disabled"), "darkgray", "disabled,=,1"]; + return [__("Disabled"), "gray", "disabled,=,1"]; } else if (!doc.batch_qty) { - return [__("Empty"), "darkgray", "batch_qty,=,0|disabled,=,0"]; + return [__("Empty"), "gray", "batch_qty,=,0|disabled,=,0"]; } else if (doc.expiry_date && frappe.datetime.get_diff(doc.expiry_date, frappe.datetime.nowdate()) <= 0) { return [__("Expired"), "red", "expiry_date,not in,|expiry_date,<=,Today|batch_qty,>,0|disabled,=,0"] } else { diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_list.js b/erpnext/stock/doctype/delivery_note/delivery_note_list.js index 1fd12f187a..98ababa075 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note_list.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note_list.js @@ -3,7 +3,7 @@ frappe.listview_settings['Delivery Note'] = { "transporter_name", "grand_total", "is_return", "status", "currency"], get_indicator: function(doc) { if(cint(doc.is_return)==1) { - return [__("Return"), "darkgray", "is_return,=,Yes"]; + return [__("Return"), "gray", "is_return,=,Yes"]; } else if (doc.status === "Closed") { return [__("Closed"), "green", "status,=,Closed"]; } else if (flt(doc.per_billed, 2) < 100) { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js index f294ce9fcc..aed2e4968f 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js @@ -3,7 +3,7 @@ frappe.listview_settings['Purchase Receipt'] = { "transporter_name", "is_return", "status", "per_billed", "currency"], get_indicator: function(doc) { if(cint(doc.is_return)==1) { - return [__("Return"), "darkgray", "is_return,=,Yes"]; + return [__("Return"), "gray", "is_return,=,Yes"]; } else if (doc.status === "Closed") { return [__("Closed"), "green", "status,=,Closed"]; } else if (flt(doc.grand_total) !== 0 && flt(doc.per_billed, 2) < 100) { diff --git a/erpnext/support/doctype/issue/issue_list.js b/erpnext/support/doctype/issue/issue_list.js index 2a8f05dc42..e04498e29e 100644 --- a/erpnext/support/doctype/issue/issue_list.js +++ b/erpnext/support/doctype/issue/issue_list.js @@ -28,7 +28,7 @@ frappe.listview_settings['Issue'] = { } else if (doc.status === 'Closed') { return [__(doc.status), "green", "status,=," + doc.status]; } else { - return [__(doc.status), "darkgray", "status,=," + doc.status]; + return [__(doc.status), "gray", "status,=," + doc.status]; } } } diff --git a/erpnext/templates/includes/address_row.html b/erpnext/templates/includes/address_row.html index 01aea9100a..6d4dd5444c 100644 --- a/erpnext/templates/includes/address_row.html +++ b/erpnext/templates/includes/address_row.html @@ -2,7 +2,7 @@
- {{ doc.address_title }} + {{ doc.address_title }}
{{ _(doc.address_type) }}
{{ doc.city }}
diff --git a/erpnext/templates/includes/issue_row.html b/erpnext/templates/includes/issue_row.html index e41e78abc6..d909c5feea 100644 --- a/erpnext/templates/includes/issue_row.html +++ b/erpnext/templates/includes/issue_row.html @@ -2,7 +2,7 @@
- {% set indicator = 'red' if doc.status == 'Open' else 'darkgray' %} + {% set indicator = 'red' if doc.status == 'Open' else 'gray' %} {% set indicator = 'green' if doc.status == 'Closed' else indicator %} {{ doc.name }} @@ -10,7 +10,7 @@
{{ doc.subject }}
- {% set indicator = 'red' if doc.status == 'Open' else 'darkgray' %} + {% set indicator = 'red' if doc.status == 'Open' else 'gray' %} {% set indicator = 'green' if doc.status == 'Closed' else indicator %} {% set indicator = 'orange' if doc.status == 'Open' and doc.priority == 'Medium' else indicator %} {% set indicator = 'yellow' if doc.status == 'Open' and doc.priority == 'Low' else indicator %} diff --git a/erpnext/templates/includes/projects/project_row.html b/erpnext/templates/includes/projects/project_row.html index 3fd102a737..4c8c40db00 100644 --- a/erpnext/templates/includes/projects/project_row.html +++ b/erpnext/templates/includes/projects/project_row.html @@ -15,7 +15,7 @@
{% else %} - + {{ doc.status }} {% endif %}
diff --git a/erpnext/templates/includes/projects/project_tasks.html b/erpnext/templates/includes/projects/project_tasks.html index 2f148dd58d..50b9f4b259 100644 --- a/erpnext/templates/includes/projects/project_tasks.html +++ b/erpnext/templates/includes/projects/project_tasks.html @@ -3,7 +3,7 @@
- {{ task.subject }} + {{ task.subject }}
{{ _("modified") }} {{ frappe.utils.pretty_date(task.modified) }} diff --git a/erpnext/templates/includes/projects/project_timesheets.html b/erpnext/templates/includes/projects/project_timesheets.html index e2a9c60069..05a07c12e8 100644 --- a/erpnext/templates/includes/projects/project_timesheets.html +++ b/erpnext/templates/includes/projects/project_timesheets.html @@ -3,7 +3,7 @@
- {{ timesheet.info.name }} + {{ timesheet.info.name }}
{{ _("From") }} {{ frappe.format_date(timesheet.from_time) }} {{ _("to") }} {{ frappe.format_date(timesheet.to_time) }}
diff --git a/erpnext/templates/includes/timesheet/timesheet_row.html b/erpnext/templates/includes/timesheet/timesheet_row.html index 12bbbfb3ae..0f9cc77e89 100644 --- a/erpnext/templates/includes/timesheet/timesheet_row.html +++ b/erpnext/templates/includes/timesheet/timesheet_row.html @@ -1,7 +1,7 @@
- + {{ doc.name }}
diff --git a/erpnext/templates/includes/transaction_row.html b/erpnext/templates/includes/transaction_row.html index 1f5cb4e92d..383413103e 100644 --- a/erpnext/templates/includes/transaction_row.html +++ b/erpnext/templates/includes/transaction_row.html @@ -1,7 +1,7 @@
- + {{ doc.name }}
diff --git a/erpnext/templates/pages/material_request_info.html b/erpnext/templates/pages/material_request_info.html index fce4a40498..b6399e7481 100644 --- a/erpnext/templates/pages/material_request_info.html +++ b/erpnext/templates/pages/material_request_info.html @@ -20,7 +20,7 @@
- + {{ _(doc.get('indicator_title')) or _(doc.status) or _("Submitted") }}
diff --git a/erpnext/templates/pages/order.html b/erpnext/templates/pages/order.html index 9941446685..3db05bdeab 100644 --- a/erpnext/templates/pages/order.html +++ b/erpnext/templates/pages/order.html @@ -34,7 +34,7 @@
- + {{ _(doc.get('indicator_title')) or _(doc.status) or _("Submitted") }}
diff --git a/erpnext/templates/pages/rfq.html b/erpnext/templates/pages/rfq.html index 7e7e7f4266..6e2edb6391 100644 --- a/erpnext/templates/pages/rfq.html +++ b/erpnext/templates/pages/rfq.html @@ -77,13 +77,13 @@
- {{d.name}} + {{d.name}}
- {{d.status}} + {{d.status}}
- {{d.transaction_date}} + {{d.transaction_date}}
Link From a49af09705f96fc44464b8fc766d43d4210c82c3 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 27 Aug 2020 18:57:43 +0530 Subject: [PATCH 016/728] fix: Primary button icon --- erpnext/accounts/doctype/account/account_tree.js | 2 +- erpnext/support/doctype/issue/issue.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index 28b090bdad..ea8ca0b7d8 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -120,7 +120,7 @@ frappe.treeview_settings["Account"] = { } else { treeview.new_node(); } - }, "octicon octicon-plus"); + }, "add"); }, onrender: function(node) { if(frappe.boot.user.can_read.indexOf("GL Entry") !== -1){ diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 9e15757ce0..95ff197802 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -164,7 +164,7 @@ frappe.ui.form.on("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")){ + if (!frm.timeline.wrapper.data("split-issue-event-attached")) { frm.timeline.wrapper.on('click', '.btn-split-issue', (e) => { var dialog = new frappe.ui.Dialog({ title: __("Split Issue"), From a324e9bd1252da3abadb0c2ae556c34d593d14d1 Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 27 Aug 2020 19:26:14 +0530 Subject: [PATCH 017/728] fix: remove text-muted and small class fromtree items --- erpnext/accounts/doctype/account/account_tree.js | 2 +- .../healthcare_service_unit/healthcare_service_unit_tree.js | 6 +++--- erpnext/stock/doctype/warehouse/warehouse_tree.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index ea8ca0b7d8..e7539f86ed 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -130,7 +130,7 @@ frappe.treeview_settings["Account"] = { let dr_or_cr = balance > 0 ? "Dr": "Cr"; if (node.data && node.data.balance!==undefined) { - $('' + $('' + (node.data.balance_in_account_currency ? (format_currency(Math.abs(node.data.balance_in_account_currency), node.data.account_currency) + " / ") : "") diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js index a03b579c50..bfb0ba13ce 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js @@ -13,19 +13,19 @@ frappe.treeview_settings["Healthcare Service Unit"] = { ignore_fields:["parent_healthcare_service_unit"], onrender: function(node) { if (node.data.occupied_out_of_vacant!==undefined){ - $('' + $('' + " " + node.data.occupied_out_of_vacant + '').insertBefore(node.$ul); } if (node.data && node.data.inpatient_occupancy!==undefined) { if (node.data.inpatient_occupancy == 1){ if (node.data.occupancy_status == "Occupied"){ - $('' + $('' + " " + node.data.occupancy_status + '').insertBefore(node.$ul); } if (node.data.occupancy_status == "Vacant"){ - $('' + $('' + " " + node.data.occupancy_status + '').insertBefore(node.$ul); } diff --git a/erpnext/stock/doctype/warehouse/warehouse_tree.js b/erpnext/stock/doctype/warehouse/warehouse_tree.js index 918d2f1559..3665c0530f 100644 --- a/erpnext/stock/doctype/warehouse/warehouse_tree.js +++ b/erpnext/stock/doctype/warehouse/warehouse_tree.js @@ -19,7 +19,7 @@ frappe.treeview_settings['Warehouse'] = { ignore_fields:["parent_warehouse"], onrender: function(node) { if (node.data && node.data.balance!==undefined) { - $('' + $('' + format_currency(Math.abs(node.data.balance), node.data.company_currency) + '').insertBefore(node.$ul); } From a6cb27cb8e89df0b959bdcb7c64250e86fac1fb7 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 28 Aug 2020 13:10:46 +0530 Subject: [PATCH 018/728] chore: trigger build From fa400820bb619ffd7c95e23a8208e2003226aa6d Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 28 Aug 2020 15:02:04 +0530 Subject: [PATCH 019/728] fix: move leaderboard style to frappe --- erpnext/public/less/erpnext.less | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/erpnext/public/less/erpnext.less b/erpnext/public/less/erpnext.less index 1488bdd9c7..4076ebec1f 100644 --- a/erpnext/public/less/erpnext.less +++ b/erpnext/public/less/erpnext.less @@ -447,20 +447,6 @@ body[data-route="pos"] { } -// Leaderboard - -.leaderboard { - .result { - border-top: 1px solid #d1d8dd; - } - .list-item { - padding-left: 45px; - } - .list-item_content { - padding-right: 45px; - } -} - // Healthcare .exercise-card { From 288ced24dbf771988b1c601d2e6d5f0fde07bd12 Mon Sep 17 00:00:00 2001 From: michellealva Date: Sun, 30 Aug 2020 19:33:27 +0530 Subject: [PATCH 020/728] feat: Add Naming Series for Project DocType --- erpnext/projects/doctype/project/project.json | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index f3cecd9059..122a1a96f4 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -2,12 +2,13 @@ "actions": [], "allow_import": 1, "allow_rename": 1, - "autoname": "field:project_name", + "autoname": "naming_series:", "creation": "2013-03-07 11:55:07", "doctype": "DocType", "document_type": "Setup", "engine": "InnoDB", "field_order": [ + "naming_series", "project_name", "status", "project_type", @@ -440,13 +441,22 @@ "fieldtype": "Text", "label": "Message", "mandatory_depends_on": "collect_progress" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "hidden": 1, + "label": "Series", + "options": "PROJ.####", + "set_only_once": 1 } ], "icon": "fa fa-puzzle-piece", "idx": 29, + "index_web_pages_for_search": 1, "links": [], "max_attachments": 4, - "modified": "2020-04-08 22:11:14.552615", + "modified": "2020-08-30 19:32:40.050707", "modified_by": "Administrator", "module": "Projects", "name": "Project", @@ -488,5 +498,6 @@ "sort_field": "modified", "sort_order": "DESC", "timeline_field": "customer", + "title_field": "project_name", "track_seen": 1 -} +} \ No newline at end of file From f46c1c5164b44e7f8ce3ad65a90189688f291740 Mon Sep 17 00:00:00 2001 From: michellealva Date: Sun, 30 Aug 2020 21:37:45 +0530 Subject: [PATCH 021/728] fix: Change property of field --- erpnext/projects/doctype/project/project.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 122a1a96f4..c91b01eaa9 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -445,9 +445,9 @@ { "fieldname": "naming_series", "fieldtype": "Select", - "hidden": 1, "label": "Series", "options": "PROJ.####", + "reqd": 1, "set_only_once": 1 } ], @@ -456,7 +456,7 @@ "index_web_pages_for_search": 1, "links": [], "max_attachments": 4, - "modified": "2020-08-30 19:32:40.050707", + "modified": "2020-08-30 21:36:45.915818", "modified_by": "Administrator", "module": "Projects", "name": "Project", From df00c5c84601e9a7b3ccf5f423671c1fcb59a9d3 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 1 Sep 2020 13:55:34 +0530 Subject: [PATCH 022/728] fix: Update erpnext logo --- erpnext/hooks.py | 2 +- erpnext/public/images/erpnext-logo.svg | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 erpnext/public/images/erpnext-logo.svg diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 1c36c2a30b..b71c4652d1 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -10,7 +10,7 @@ app_color = "#e74c3c" app_email = "info@erpnext.com" app_license = "GNU General Public License (v3)" source_link = "https://github.com/frappe/erpnext" -app_logo_url = '/assets/erpnext/images/erpnext_logo.svg' +app_logo_url = '/assets/erpnext/images/erpnext-logo.svg' develop_version = '13.x.x-develop' diff --git a/erpnext/public/images/erpnext-logo.svg b/erpnext/public/images/erpnext-logo.svg new file mode 100644 index 0000000000..a3ac3bb2ce --- /dev/null +++ b/erpnext/public/images/erpnext-logo.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From f51cf9f23e0e41e6db5b5538ba2c4f75b24bfe74 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 2 Sep 2020 11:43:36 +0530 Subject: [PATCH 023/728] fix: Crop Cycle Test --- erpnext/agriculture/doctype/crop_cycle/crop_cycle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py index cae150c428..afbd9b4e6e 100644 --- a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py +++ b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py @@ -48,7 +48,7 @@ class CropCycle(Document): def import_disease_tasks(self, disease, start_date): disease_doc = frappe.get_doc('Disease', disease) - self.create_task(disease_doc.treatment_task, self.name, start_date) + self.create_task(disease_doc.treatment_task, self.project, start_date) def create_project(self, period, crop_tasks): project = frappe.get_doc({ From 099d6718c914198475153bf6498cc57ed6fd6396 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 2 Sep 2020 11:54:55 +0530 Subject: [PATCH 024/728] fix: Dont Copy or Print Naming Series --- erpnext/projects/doctype/project/project.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index c91b01eaa9..8ed6888854 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -446,7 +446,9 @@ "fieldname": "naming_series", "fieldtype": "Select", "label": "Series", + "no_copy": 1, "options": "PROJ.####", + "print_hide": 1, "reqd": 1, "set_only_once": 1 } @@ -456,7 +458,7 @@ "index_web_pages_for_search": 1, "links": [], "max_attachments": 4, - "modified": "2020-08-30 21:36:45.915818", + "modified": "2020-09-02 11:54:01.223620", "modified_by": "Administrator", "module": "Projects", "name": "Project", From d8c38249e03e2206c41cd86c64d31cae431845e5 Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Thu, 3 Sep 2020 09:04:21 +0530 Subject: [PATCH 025/728] fix: Change naming series Co-authored-by: Himanshu --- erpnext/projects/doctype/project/project.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 8ed6888854..3cdfcb212f 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -447,7 +447,7 @@ "fieldtype": "Select", "label": "Series", "no_copy": 1, - "options": "PROJ.####", + "options": "PROJ-.####", "print_hide": 1, "reqd": 1, "set_only_once": 1 @@ -502,4 +502,4 @@ "timeline_field": "customer", "title_field": "project_name", "track_seen": 1 -} \ No newline at end of file +} From e35fd5e3051b1ce84417c068d8e5bedf738c8620 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 8 Sep 2020 16:24:10 +0530 Subject: [PATCH 026/728] 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 027/728] 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 028/728] 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 029/728] 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 030/728] 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 031/728] 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 2b405b9bb3118f15f9a68de383fed878fed5a05d Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 10 Sep 2020 15:37:54 +0530 Subject: [PATCH 032/728] feat: add icon to healthcare desk page --- erpnext/healthcare/desk_page/healthcare/healthcare.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 6546b08db9..353d86f4d7 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -61,10 +61,11 @@ "doctype": "Desk Page", "extends_another_page": 0, "hide_custom": 0, + "icon": "healthcare", "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-06-25 23:50:56.951698", + "modified": "2020-09-10 15:37:23.666787", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", @@ -75,7 +76,7 @@ "restrict_to_domain": "Healthcare", "shortcuts": [ { - "color": "#ffe8cd", + "color": "Orange", "format": "{} Open", "label": "Patient Appointment", "link_to": "Patient Appointment", @@ -83,7 +84,7 @@ "type": "DocType" }, { - "color": "#ffe8cd", + "color": "Orange", "format": "{} Active", "label": "Patient", "link_to": "Patient", @@ -91,7 +92,7 @@ "type": "DocType" }, { - "color": "#cef6d1", + "color": "Green", "format": "{} Vacant", "label": "Healthcare Service Unit", "link_to": "Healthcare Service Unit", From 67095d239f27e2e18ae3c61f183b84dcba911976 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 10 Sep 2020 17:15:52 +0530 Subject: [PATCH 033/728] fix: Custom dashboard sections --- .../opening_invoice_creation_tool.js | 3 ++- .../opening_invoice_creation_tool_dashboard.html | 1 - .../assets/doctype/asset_maintenance/asset_maintenance.js | 5 ++--- erpnext/hr/doctype/leave_application/leave_application.js | 3 ++- .../leave_application/leave_application_dashboard.html | 1 - erpnext/stock/doctype/batch/batch.js | 3 +-- erpnext/stock/doctype/item/item.js | 3 +-- 7 files changed, 8 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js index 699eb08e17..08e30b52e2 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js @@ -101,7 +101,8 @@ frappe.ui.form.on('Opening Invoice Creation Tool', { frappe.render_template('opening_invoice_creation_tool_dashboard', { data: opening_invoices_summary, max_count: max_count - }) + }), + __("Opening Invoices Summary") ); section.on('click', '.invoice-link', function() { diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool_dashboard.html b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool_dashboard.html index 5b136d4f66..afbcfa5602 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool_dashboard.html +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool_dashboard.html @@ -1,4 +1,3 @@ -
{{ __("Opening Invoices Summary") }}
{% $.each(data, (company, summary) => { %}
{{ company }}
diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js index 001fc26ffe..70b8654509 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js @@ -40,14 +40,13 @@ frappe.ui.form.on('Asset Maintenance', { if(!r.message) { return; } - var section = frm.dashboard.add_section(`
- ${ __("Maintenance Log") }
`); + const section = frm.dashboard.add_section('', __("Maintenance Log")); var rows = $('
').appendTo(section); // show (r.message || []).forEach(function(d) { $(`
- ${d.maintenance_status} ${d.count} diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index d62e418b17..9ccb915908 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -75,7 +75,8 @@ frappe.ui.form.on("Leave Application", { frm.dashboard.add_section( frappe.render_template('leave_application_dashboard', { data: leave_details - }) + }), + __("Allocated Leaves") ); frm.dashboard.show(); let allowed_leave_types = Object.keys(leave_details); diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html index d30e3b9f9c..6324b04927 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html +++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html @@ -1,6 +1,5 @@ {% if not jQuery.isEmptyObject(data) %} -
{{ __("Allocated Leaves") }}
diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index e2ea7f992c..71a3e7abca 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -47,8 +47,7 @@ frappe.ui.form.on('Batch', { return; } - var section = frm.dashboard.add_section(`
- ${ __("Stock Levels") }
`); + const section = frm.dashboard.add_section('', __("Stock Levels")); // sort by qty r.message.sort(function(a, b) { a.qty > b.qty ? 1 : -1 }); diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 38e5fe53a7..e07e653703 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -380,8 +380,7 @@ $.extend(erpnext.item, { // Show Stock Levels only if is_stock_item if (frm.doc.is_stock_item) { frappe.require('assets/js/item-dashboard.min.js', function() { - var section = frm.dashboard.add_section('
\ - ' + __("Stock Levels") + '
'); + const section = frm.dashboard.add_section('', __("Stock Levels")); erpnext.item.item_dashboard = new erpnext.stock.ItemDashboard({ parent: section, item_code: frm.doc.name From a119688e32f0a56ae546d75cdd047b2190308fa7 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 24 Sep 2020 15:22:46 +0530 Subject: [PATCH 034/728] 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 035/728] 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 1323a9a990ec8f4f05dcd997e52216a55eb93b34 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 30 Sep 2020 16:33:14 +0530 Subject: [PATCH 036/728] feat: set app name for ERPNext in install and patch --- erpnext/patches.txt | 1 + erpnext/patches/v13_0/set_app_name.py | 8 ++++++++ erpnext/setup/install.py | 5 +++++ 3 files changed, 14 insertions(+) create mode 100644 erpnext/patches/v13_0/set_app_name.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 6087ce29aa..95dfff797a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -729,3 +729,4 @@ erpnext.patches.v13_0.setting_custom_roles_for_some_regional_reports erpnext.patches.v13_0.rename_issue_doctype_fields erpnext.patches.v13_0.change_default_pos_print_format erpnext.patches.v13_0.set_youtube_video_id +erpnext.patches.v13_0.set_app_name diff --git a/erpnext/patches/v13_0/set_app_name.py b/erpnext/patches/v13_0/set_app_name.py new file mode 100644 index 0000000000..0c78b65770 --- /dev/null +++ b/erpnext/patches/v13_0/set_app_name.py @@ -0,0 +1,8 @@ +import frappe +from frappe import _ + +def execute(): + frappe.reload_doctype("System Settings") + settings = frappe.get_doc("System Settings") + settings.app_name = _("ERPNext") + settings.save() \ No newline at end of file diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 2225fe169f..0bb480bd4b 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -28,6 +28,7 @@ def after_install(): create_default_energy_point_rules() add_company_to_session_defaults() add_standard_navbar_items() + add_app_name() frappe.db.commit() @@ -158,3 +159,7 @@ def add_standard_navbar_items(): }) navbar_settings.save() + +def add_app_name(): + settings = frappe.get_doc("System Settings") + settings.app_name = _("ERPNext") \ No newline at end of file From 6a506b63bf5b4928481c9f5768bc847d30c464d6 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 5 Oct 2020 12:41:30 +0530 Subject: [PATCH 037/728] fix: variables path --- erpnext/public/scss/website.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/scss/website.scss b/erpnext/public/scss/website.scss index 617e916724..24a1b3780c 100644 --- a/erpnext/public/scss/website.scss +++ b/erpnext/public/scss/website.scss @@ -1,4 +1,4 @@ -@import "frappe/public/scss/variables"; +@import "frappe/public/scss/website/variables"; .product-image img { min-height: 20rem; From 4be5b5c891066e469c1d4458e04368e218d8ccd4 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 8 Oct 2020 19:08:27 +0530 Subject: [PATCH 038/728] 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 73d944da21045d1f6387b5fc583e37e37850c30d Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 13 Oct 2020 18:11:05 +0530 Subject: [PATCH 039/728] fix: review changes --- erpnext/selling/doctype/quotation/quotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 7c55d7742f..3157982d52 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -62,7 +62,7 @@ class Quotation(SellingController): opportunity = self.opportunity opp = frappe.get_doc("Opportunity", opportunity) - opp.status = status + opp.set_status(status=status) opp.set_status(update=True) def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None): From cb8457b9ae8de30522b132cbc97f8e3326227602 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 15 Oct 2020 13:53:12 +0530 Subject: [PATCH 040/728] feat: add description to Accounts onboarding --- erpnext/accounts/module_onboarding/accounts/accounts.json | 2 +- .../chart_of_accounts/chart_of_accounts.json | 6 +++++- .../configure_account_settings.json | 3 ++- .../create_a_customer/create_a_customer.json | 4 +++- .../onboarding_step/create_a_product/create_a_product.json | 3 ++- .../create_a_supplier/create_a_supplier.json | 3 ++- .../create_your_first_purchase_invoice.json | 3 ++- .../create_your_first_sales_invoice.json | 3 ++- .../accounts/onboarding_step/setup_taxes/setup_taxes.json | 3 ++- erpnext/stock/module_onboarding/stock/stock.json | 2 +- .../onboarding_step/create_a_product/create_a_product.json | 2 +- .../create_a_purchase_receipt.json | 2 +- .../create_a_stock_entry/create_a_stock_entry.json | 2 +- .../create_a_supplier/create_a_supplier.json | 2 +- .../introduction_to_stock_entry.json | 2 +- .../setup_your_warehouse/setup_your_warehouse.json | 2 +- .../onboarding_step/stock_settings/stock_settings.json | 2 +- 17 files changed, 29 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/module_onboarding/accounts/accounts.json b/erpnext/accounts/module_onboarding/accounts/accounts.json index ba1a779b4c..98272c3891 100644 --- a/erpnext/accounts/module_onboarding/accounts/accounts.json +++ b/erpnext/accounts/module_onboarding/accounts/accounts.json @@ -13,7 +13,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts", "idx": 0, "is_complete": 0, - "modified": "2020-07-08 14:06:09.033880", + "modified": "2020-10-15 13:52:40.068450", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts", diff --git a/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json index cbd022bfdb..a1532302a1 100644 --- a/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json +++ b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json @@ -1,14 +1,18 @@ { "action": "Go to Page", + "callback_message": "Great! Let's move to the next step!", + "callback_title": "Awesome Work", "creation": "2020-05-13 19:58:20.928127", + "description": "# Chart Of Accounts\n\n**The Chart of Accounts is the blueprint of the accounts in your organization.**\n\nThe overall structure of your Chart of Accounts is based on a system of double entry\naccounting that has become a standard all over the world to quantify how a\ncompany is doing financially.\n\nChart of Accounts is a tree view of the names of the Accounts (Ledgers and\nGroups) that a Company requires to manage its books of accounts. ERPNext sets\nup a simple chart of accounts for each Company you create, but you can\nmodify it according to your needs and legal requirements.\n\nFor each company, Chart of Accounts signifies the way to classify the accounting entries, mostly\nbased on statutory (tax, compliance to government regulations) requirements.\n\nThe Chart of Accounts helps you to answer questions like:\n\n * What is your organization worth?\n * How much debt have you taken?\n * How much profit are you making (and hence paying tax)?\n * How much are you selling?\n * What is your expense break-up?\n", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, + "intro_video_url": "https://www.youtube.com/embed/AcfMCT7wLLo", "is_complete": 0, "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 17:40:28.410447", + "modified": "2020-10-15 13:12:04.771355", "modified_by": "Administrator", "name": "Chart Of Accounts", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json b/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json index c8be357de0..1e8f1034a3 100644 --- a/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json +++ b/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json @@ -1,6 +1,7 @@ { "action": "Create Entry", "creation": "2020-05-14 17:53:00.876946", + "description": "# Account Settings\n\nThis is a crucial piece of configuration. There are various account settings in ERPNext to restrict and configure actions in the Accounting module.\n\nThe following settings are avaialble for you to configure\n\n1. Account Freezing \n2. Credit and Overbilling\n3. Invoicing and Tax Automations\n4. Balance Sheet configurations\n\nThere's much more, you can check it all out in this step", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -8,7 +9,7 @@ "is_mandatory": 0, "is_single": 1, "is_skipped": 0, - "modified": "2020-05-14 18:06:25.212923", + "modified": "2020-10-15 13:45:14.687458", "modified_by": "Administrator", "name": "Configure Account Settings", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json index 5a403b06cf..02434a09bf 100644 --- a/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json +++ b/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json @@ -1,14 +1,16 @@ { "action": "Create Entry", "creation": "2020-05-14 17:46:41.831517", + "description": "# Customer\n\nA customer, who is sometimes known as a client, buyer, or purchaser is the one who receives goods, services, products, or ideas, from a seller for a monetary consideration.\n\n### Creating a customer is easy and can be done in the following steps\n\n1. Go to the Customer list and click on New.\n2. Enter Full Name of the customer.\n3. Select **Company** if the customer represents a company or **Individual** otherwise in Type field.\n4. Select a Customer Group. A few groups are included by default, you can create additional groups if you need.\n5. Select the Territory.\n6. If the customer is being created against a lead, you can select the same in From Lead field.\n7. Save.\n\nUp next is a video about customers and suppliers that will give you more clarity on these concepts in ERPNext", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, + "intro_video_url": "https://www.youtube.com/watch?v=zsrrVDk6VBs", "is_complete": 0, "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-06-01 13:16:19.731719", + "modified": "2020-10-15 13:37:59.699228", "modified_by": "Administrator", "name": "Create a Customer", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json b/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json index d2068e167b..3214d8ecf1 100644 --- a/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json +++ b/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json @@ -1,6 +1,7 @@ { "action": "Create Entry", "creation": "2020-05-12 18:16:06.624554", + "description": "## Creating Products\n\nIn ERPNext, any product or a service offered by your company is called an Item. The term Item is also applicable to raw materials or components of products yet to be produced (before they can be sold to customers). ERPNext allows you to manage all sorts of items like raw-materials, sub-assemblies, finished goods, item variants, and service items.\n\nERPNext is optimized for itemized management of your sales and purchase. If you are in services, you can create an Item for each service that you offer. Completing the Item Master is very essential for the successful implementation of ERPNext.\n\nYou can access the Item section\n\n`Desk > Stock > Item`\n\n\n", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -8,7 +9,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 18:30:02.489949", + "modified": "2020-10-15 13:30:46.817174", "modified_by": "Administrator", "name": "Create a Product", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json index 7a64224bd4..62d643aa72 100644 --- a/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json @@ -1,6 +1,7 @@ { "action": "Create Entry", "creation": "2020-05-14 22:09:10.043554", + "description": "## Let's add your Suppliers\n\nSuppliers are companies or individuals who provide you with products or services. A supplier may be distinguished from a contractor or subcontractor, who commonly adds specialized input to deliverables. A supplier is also known as a vendor. There are different types of suppliers based on the goods and products they supply.\n\nERPNext allows you to create your own categories of suppliers. These categories are known as Supplier Groups. For example, if your suppliers are mainly pharmaceutical companies and FMCG distributors, you can create a new Supplier Groups for them and name the groups accordingly.", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -8,7 +9,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 22:09:10.043554", + "modified": "2020-10-15 13:32:39.651700", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json index 3a2b8d3925..2e753d63b3 100644 --- a/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json +++ b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json @@ -1,6 +1,7 @@ { "action": "Create Entry", "creation": "2020-05-14 22:10:07.049704", + "description": "# What's a Purchase Invoice?\n\nA Purchase Invoice is a bill you receive from your Suppliers against which you need to make the payment.\nPurchase Invoice is the exact opposite of your Sales Invoice. Here you accrue expenses to your Supplier. Making a Purchase Invoice is very similar to making a Purchase Order.\n\n![Purchase Flow](https://docs.erpnext.com/docs/assets/img/accounts/pi-flow.png)\n\n", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -8,7 +9,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 22:10:07.049704", + "modified": "2020-10-15 13:33:56.079882", "modified_by": "Administrator", "name": "Create Your First Purchase Invoice", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json b/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json index 473de5079f..bcf2c56dad 100644 --- a/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json +++ b/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json @@ -1,6 +1,7 @@ { "action": "Create Entry", "creation": "2020-05-14 17:48:21.019019", + "description": "# All about sales invoice\n\nA Sales Invoice is a bill that you send to your Customers against which the Customer makes the payment. Sales Invoice is an accounting transaction. On submission of Sales Invoice, the system updates the receivable and books income against a Customer Account.\n\nHere's the flow of how a sales invoice is generally created\n\n\n![Sales Flow](https://docs.erpnext.com/docs/assets/img/accounts/so-flow.png)", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -8,7 +9,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 17:48:21.019019", + "modified": "2020-10-15 13:39:43.970254", "modified_by": "Administrator", "name": "Create Your First Sales Invoice", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json index 8e0006762d..2eedf56a83 100644 --- a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json +++ b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json @@ -1,6 +1,7 @@ { "action": "Create Entry", "creation": "2020-05-13 19:29:43.844463", + "description": "# Setting up Taxes\n\nOne of the primary motivators for compulsory use of accounting tools is the calculation of taxes. ERPNext allows you to make configurable tax templates that you can apply to your sales or purchase transactions.\n\nThe templates created from this form can be used in Sales Orders and Sales Invoices. The way ERPNext sets up taxes is via templates. Other types of charges that may apply to your invoices (like shipping, insurance etc.) can also be configured as taxes.\n\nFor Tax Accounts that you want to use in the tax templates, go to:\n\n`> Home > Accounting > Chart of Accounts`\n\nSelect an account and click on edit. Select the 'Account Type' as 'Tax' for the account.\nIn this step we will guide you towards making the sales and taxes template.", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -8,7 +9,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 17:40:16.014413", + "modified": "2020-10-15 13:21:17.333438", "modified_by": "Administrator", "name": "Setup Taxes", "owner": "Administrator", diff --git a/erpnext/stock/module_onboarding/stock/stock.json b/erpnext/stock/module_onboarding/stock/stock.json index 1d5bf8c97c..847464822b 100644 --- a/erpnext/stock/module_onboarding/stock/stock.json +++ b/erpnext/stock/module_onboarding/stock/stock.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/stock", "idx": 0, "is_complete": 0, - "modified": "2020-07-08 14:22:07.951891", + "modified": "2020-10-14 14:54:42.741971", "modified_by": "Administrator", "module": "Stock", "name": "Stock", diff --git a/erpnext/stock/onboarding_step/create_a_product/create_a_product.json b/erpnext/stock/onboarding_step/create_a_product/create_a_product.json index d2068e167b..335137d852 100644 --- a/erpnext/stock/onboarding_step/create_a_product/create_a_product.json +++ b/erpnext/stock/onboarding_step/create_a_product/create_a_product.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 18:30:02.489949", + "modified": "2020-10-14 14:53:00.133574", "modified_by": "Administrator", "name": "Create a Product", "owner": "Administrator", diff --git a/erpnext/stock/onboarding_step/create_a_purchase_receipt/create_a_purchase_receipt.json b/erpnext/stock/onboarding_step/create_a_purchase_receipt/create_a_purchase_receipt.json index b7811a46df..9012493f57 100644 --- a/erpnext/stock/onboarding_step/create_a_purchase_receipt/create_a_purchase_receipt.json +++ b/erpnext/stock/onboarding_step/create_a_purchase_receipt/create_a_purchase_receipt.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 18:59:13.266713", + "modified": "2020-10-14 14:53:25.618434", "modified_by": "Administrator", "name": "Create a Purchase Receipt", "owner": "Administrator", diff --git a/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json b/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json index 2b83f657d6..09902b8844 100644 --- a/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json +++ b/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-15 03:30:58.047696", + "modified": "2020-10-14 14:53:00.105905", "modified_by": "Administrator", "name": "Create a Stock Entry", "owner": "Administrator", diff --git a/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json index 7a64224bd4..ef61fa3b2e 100644 --- a/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 22:09:10.043554", + "modified": "2020-10-14 14:53:00.120455", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", diff --git a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json index 009a44f6e4..212e5055ed 100644 --- a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json +++ b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-26 15:55:41.457289", + "modified": "2020-10-14 14:53:00.075177", "modified_by": "Administrator", "name": "Introduction to Stock Entry", "owner": "Administrator", diff --git a/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json b/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json index 9457deee26..75940ed2a6 100644 --- a/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json +++ b/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-07-04 12:33:16.970031", + "modified": "2020-10-14 14:53:25.538900", "modified_by": "Administrator", "name": "Setup your Warehouse", "owner": "Administrator", diff --git a/erpnext/stock/onboarding_step/stock_settings/stock_settings.json b/erpnext/stock/onboarding_step/stock_settings/stock_settings.json index 7591bff538..ae34afa695 100644 --- a/erpnext/stock/onboarding_step/stock_settings/stock_settings.json +++ b/erpnext/stock/onboarding_step/stock_settings/stock_settings.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 1, "is_skipped": 0, - "modified": "2020-05-15 03:55:15.444151", + "modified": "2020-10-14 14:53:00.092504", "modified_by": "Administrator", "name": "Stock Settings", "owner": "Administrator", From 12d4be7f3f9303f7ad42596cf26b1b2aaf159424 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 16 Oct 2020 11:31:47 +0530 Subject: [PATCH 041/728] feat: icons in leaderboard --- erpnext/startup/leaderboard.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/startup/leaderboard.py b/erpnext/startup/leaderboard.py index ef238f1165..8819a55c0a 100644 --- a/erpnext/startup/leaderboard.py +++ b/erpnext/startup/leaderboard.py @@ -12,6 +12,7 @@ def get_leaderboards(): {'fieldname': 'outstanding_amount', 'fieldtype': 'Currency'} ], "method": "erpnext.startup.leaderboard.get_all_customers", + "icon": "customer" }, "Item": { "fields": [ @@ -23,6 +24,7 @@ def get_leaderboards(): {'fieldname': 'available_stock_value', 'fieldtype': 'Currency'} ], "method": "erpnext.startup.leaderboard.get_all_items", + "icon": "stock" }, "Supplier": { "fields": [ @@ -31,6 +33,7 @@ def get_leaderboards(): {'fieldname': 'outstanding_amount', 'fieldtype': 'Currency'} ], "method": "erpnext.startup.leaderboard.get_all_suppliers", + "icon": "buying" }, "Sales Partner": { "fields": [ @@ -38,12 +41,14 @@ def get_leaderboards(): {'fieldname': 'total_commission', 'fieldtype': 'Currency'} ], "method": "erpnext.startup.leaderboard.get_all_sales_partner", + "icon": "hr" }, "Sales Person": { "fields": [ {'fieldname': 'total_sales_amount', 'fieldtype': 'Currency'} ], "method": "erpnext.startup.leaderboard.get_all_sales_person", + "icon": "customer" } } From 5418818683fb1d211772cb1e0467e7355c30eb3a Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 16 Oct 2020 14:24:00 +0530 Subject: [PATCH 042/728] feat: add tour for account settings --- .../accounts_settings/accounts_settings.js | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js index 0627675de7..541901c9ab 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js @@ -6,3 +6,46 @@ frappe.ui.form.on('Accounts Settings', { } }); + +frappe.tour['Accounts Settings'] = [ + { + fieldname: "acc_frozen_upto", + title: "Accounts Frozen Upto", + description: __("Freeze accounting transactions up to specified date, nobody can make/modify entry except the specified Role."), + }, + { + fieldname: "frozen_accounts_modifier", + title: "Role Allowed to Set Frozen Accounts & Edit Frozen Entries", + description: __("Users with this Role are allowed to set frozen accounts and create/modify accounting entries against frozen accounts.") + }, + { + fieldname: "determine_address_tax_category_from", + title: "Determine Address Tax Category From", + description: __("Tax category can be set on Addresses. An address can be Shipping or Billing address. Set which addres to select when applying Tax Category.") + }, + { + fieldname: "over_billing_allowance", + title: "Over Billing Allowance Percentage", + description: __("The percentage by which you can overbill transactions. For example, if the order value is $100 for an Item and percentage here is set as 10% then you are allowed to bill for $110.") + }, + { + fieldname: "credit_controller", + title: "Credit Controller", + description: __("Select the role that is allowed to submit transactions that exceed credit limits set. The credit limit can be set in the Customer form.") + }, + { + fieldname: "make_payment_via_journal_entry", + title: "Make Payment via Journal Entry", + description: __("When checked, if user proceeds to make payment from an invoice, the system will open a Journal Entry instead of a Payment Entry.") + }, + { + fieldname: "unlink_payment_on_cancellation_of_invoice", + title: "Unlink Payment on Cancellation of Invoice", + description: __("If checked, system will unlink the payment against the respective invoice.") + }, + { + fieldname: "unlink_advance_payment_on_cancelation_of_order", + title: "Unlink Advance Payment on Cancellation of Order", + description: __("Similar to the previous option, this unlinks any advance payments made against Purchase/Sales Orders.") + } +]; \ No newline at end of file From 359778e2357997aaeac126c37bdcb34e8efa7ed3 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 16 Oct 2020 16:47:23 +0530 Subject: [PATCH 043/728] 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 8261df29c4f35fce8109d5df2258d263bcd64642 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 19 Oct 2020 13:52:35 +0530 Subject: [PATCH 044/728] feat: update splash logo --- erpnext/hooks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index b510208720..9fa60742bd 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -10,7 +10,7 @@ app_color = "#e74c3c" app_email = "info@erpnext.com" app_license = "GNU General Public License (v3)" source_link = "https://github.com/frappe/erpnext" -app_logo_url = '/assets/erpnext/images/erpnext-logo.svg' +app_logo_url = "/assets/erpnext/images/erpnext-logo.svg" develop_version = '13.x.x-develop' @@ -73,7 +73,7 @@ website_generators = ["Item Group", "Item", "BOM", "Sales Partner", website_context = { "favicon": "/assets/erpnext/images/favicon.png", - "splash_image": "/assets/erpnext/images/erp-icon.svg" + "splash_image": "/assets/erpnext/images/erpnext-logo.svg" } website_route_rules = [ From 2c2e7297ae254ee9f9c43fa5ddb5eb25b86c05d6 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 19 Oct 2020 14:44:02 +0530 Subject: [PATCH 045/728] feat: update accounts onboarding --- erpnext/accounts/module_onboarding/accounts/accounts.json | 2 +- .../onboarding_step/chart_of_accounts/chart_of_accounts.json | 4 ++-- .../configure_account_settings.json | 4 ++-- .../onboarding_step/create_a_customer/create_a_customer.json | 2 +- .../onboarding_step/create_a_product/create_a_product.json | 2 +- .../onboarding_step/create_a_supplier/create_a_supplier.json | 2 +- .../create_your_first_purchase_invoice.json | 2 +- .../create_your_first_sales_invoice.json | 2 +- erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/module_onboarding/accounts/accounts.json b/erpnext/accounts/module_onboarding/accounts/accounts.json index 98272c3891..dc5c3207b0 100644 --- a/erpnext/accounts/module_onboarding/accounts/accounts.json +++ b/erpnext/accounts/module_onboarding/accounts/accounts.json @@ -13,7 +13,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts", "idx": 0, "is_complete": 0, - "modified": "2020-10-15 13:52:40.068450", + "modified": "2020-10-19 14:43:45.080823", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts", diff --git a/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json index a1532302a1..ef20c19d56 100644 --- a/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json +++ b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json @@ -1,6 +1,6 @@ { "action": "Go to Page", - "callback_message": "Great! Let's move to the next step!", + "callback_message": "You can continue with the onboarding after exploring this page", "callback_title": "Awesome Work", "creation": "2020-05-13 19:58:20.928127", "description": "# Chart Of Accounts\n\n**The Chart of Accounts is the blueprint of the accounts in your organization.**\n\nThe overall structure of your Chart of Accounts is based on a system of double entry\naccounting that has become a standard all over the world to quantify how a\ncompany is doing financially.\n\nChart of Accounts is a tree view of the names of the Accounts (Ledgers and\nGroups) that a Company requires to manage its books of accounts. ERPNext sets\nup a simple chart of accounts for each Company you create, but you can\nmodify it according to your needs and legal requirements.\n\nFor each company, Chart of Accounts signifies the way to classify the accounting entries, mostly\nbased on statutory (tax, compliance to government regulations) requirements.\n\nThe Chart of Accounts helps you to answer questions like:\n\n * What is your organization worth?\n * How much debt have you taken?\n * How much profit are you making (and hence paying tax)?\n * How much are you selling?\n * What is your expense break-up?\n", @@ -12,7 +12,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-10-15 13:12:04.771355", + "modified": "2020-10-19 14:25:31.427339", "modified_by": "Administrator", "name": "Chart Of Accounts", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json b/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json index 1e8f1034a3..083fbb52bf 100644 --- a/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json +++ b/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json @@ -1,5 +1,5 @@ { - "action": "Create Entry", + "action": "Show Form Tour", "creation": "2020-05-14 17:53:00.876946", "description": "# Account Settings\n\nThis is a crucial piece of configuration. There are various account settings in ERPNext to restrict and configure actions in the Accounting module.\n\nThe following settings are avaialble for you to configure\n\n1. Account Freezing \n2. Credit and Overbilling\n3. Invoicing and Tax Automations\n4. Balance Sheet configurations\n\nThere's much more, you can check it all out in this step", "docstatus": 0, @@ -9,7 +9,7 @@ "is_mandatory": 0, "is_single": 1, "is_skipped": 0, - "modified": "2020-10-15 13:45:14.687458", + "modified": "2020-10-19 14:40:55.584484", "modified_by": "Administrator", "name": "Configure Account Settings", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json index 02434a09bf..4a967b140b 100644 --- a/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json +++ b/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json @@ -10,7 +10,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-10-15 13:37:59.699228", + "modified": "2020-10-16 12:59:16.989156", "modified_by": "Administrator", "name": "Create a Customer", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json b/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json index 3214d8ecf1..0cbefee858 100644 --- a/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json +++ b/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json @@ -9,7 +9,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-10-15 13:30:46.817174", + "modified": "2020-10-16 12:59:16.983833", "modified_by": "Administrator", "name": "Create a Product", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json index 62d643aa72..eede11d979 100644 --- a/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json @@ -9,7 +9,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-10-15 13:32:39.651700", + "modified": "2020-10-16 12:59:16.979176", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json index 2e753d63b3..2b53f46fe9 100644 --- a/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json +++ b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json @@ -9,7 +9,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-10-15 13:33:56.079882", + "modified": "2020-10-16 12:59:16.976334", "modified_by": "Administrator", "name": "Create Your First Purchase Invoice", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json b/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json index bcf2c56dad..d56be9220d 100644 --- a/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json +++ b/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json @@ -9,7 +9,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-10-15 13:39:43.970254", + "modified": "2020-10-16 12:59:16.987507", "modified_by": "Administrator", "name": "Create Your First Sales Invoice", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json index 2eedf56a83..fa3eabd974 100644 --- a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json +++ b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json @@ -9,7 +9,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-10-15 13:21:17.333438", + "modified": "2020-10-16 12:59:16.991287", "modified_by": "Administrator", "name": "Setup Taxes", "owner": "Administrator", From 5e00ed926cc5f74b4d4fa8ada0a83347242106f4 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 21 Oct 2020 12:30:22 +0530 Subject: [PATCH 046/728] feat: update icons --- erpnext/accounts/desk_page/accounting/accounting.json | 4 ++-- erpnext/buying/desk_page/buying/buying.json | 2 +- erpnext/selling/desk_page/selling/selling.json | 2 +- erpnext/stock/desk_page/stock/stock.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 45f75b1dcd..5de091667a 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -99,7 +99,7 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-10-08 20:31:46.022470", + "modified": "2020-10-21 12:27:51.346915", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", @@ -109,7 +109,7 @@ "pin_to_top": 0, "shortcuts": [ { - "label": "Chart of Accounts", + "label": "Chart Of Accounts", "link_to": "Account", "type": "DocType" }, diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 85f9bf6276..16df8dfdd8 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -61,7 +61,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-09-30 14:40:55.638458", + "modified": "2020-10-21 12:29:02.772723", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json index bc813bca23..82831ab61a 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -45,7 +45,7 @@ "idx": 0, "is_standard": 1, "label": "Selling", - "modified": "2020-10-08 10:23:09.984377", + "modified": "2020-10-21 12:30:12.164433", "modified_by": "Administrator", "module": "Selling", "name": "Selling", diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 3e1618d7fa..9f8f346635 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -59,7 +59,7 @@ "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-10-07 18:40:17.130207", + "modified": "2020-10-21 12:28:55.503562", "modified_by": "Administrator", "module": "Stock", "name": "Stock", From b57b3c9f6c91ed8053f2464576654a531d0f1caa Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 22 Oct 2020 15:37:47 +0530 Subject: [PATCH 047/728] feat: hook to fetch additional print settings in print view --- erpnext/controllers/buying_controller.py | 10 +---- erpnext/controllers/print_settings.py | 50 ++++++++++++++++++++++- erpnext/controllers/selling_controller.py | 11 ++--- erpnext/hooks.py | 1 + erpnext/startup/filters.py | 6 +-- 5 files changed, 57 insertions(+), 21 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index e05c70e41c..de04abbca4 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -13,6 +13,7 @@ from erpnext.stock.stock_ledger import get_valuation_rate from erpnext.stock.doctype.stock_entry.stock_entry import get_used_alternative_items from erpnext.stock.doctype.serial_no.serial_no import get_auto_serial_nos, auto_make_serial_nos, get_serial_nos from frappe.contacts.doctype.address.address import get_address_display +from erpnext.controllers.print_settings import print_settings_for_taxes from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget from erpnext.controllers.stock_controller import StockController @@ -20,14 +21,7 @@ from erpnext.controllers.stock_controller import StockController class BuyingController(StockController): def __setup__(self): if hasattr(self, "taxes"): - self.flags.print_taxes_with_zero_amount = cint(frappe.db.get_single_value("Print Settings", - "print_taxes_with_zero_amount")) - self.flags.show_inclusive_tax_in_print = self.is_inclusive_tax() - - self.print_templates = { - "total": "templates/print_formats/includes/total.html", - "taxes": "templates/print_formats/includes/taxes.html" - } + print_settings_for_taxes(self) def get_feed(self): if self.get("supplier_name"): diff --git a/erpnext/controllers/print_settings.py b/erpnext/controllers/print_settings.py index c41db25253..edd722dbe8 100644 --- a/erpnext/controllers/print_settings.py +++ b/erpnext/controllers/print_settings.py @@ -5,20 +5,31 @@ from __future__ import unicode_literals import frappe from frappe.utils import cint -def print_settings_for_item_table(doc): +def print_settings_for_item_table(doc, setting_value=None): doc.print_templates = { "qty": "templates/print_formats/includes/item_table_qty.html" } doc.hide_in_print_layout = ["uom", "stock_uom"] - doc.flags.compact_item_print = cint(frappe.db.get_single_value("Print Settings", "compact_item_print")) + doc.flags.compact_item_print = setting_value if setting_value is not None\ + else cint(frappe.db.get_single_value("Print Settings", "compact_item_print")) if doc.flags.compact_item_print: doc.print_templates["description"] = "templates/print_formats/includes/item_table_description.html" doc.flags.compact_item_fields = ["description", "qty", "rate", "amount"] doc.flags.format_columns = format_columns +def print_settings_for_taxes(doc, setting_value=None): + doc.flags.print_taxes_with_zero_amount = setting_value if setting_value is not None\ + else cint(frappe.db.get_single_value("Print Settings", "print_taxes_with_zero_amount")) + doc.flags.show_inclusive_tax_in_print = doc.is_inclusive_tax() + + doc.print_templates = { + "total": "templates/print_formats/includes/total.html", + "taxes": "templates/print_formats/includes/taxes.html" + } + def format_columns(display_columns, compact_fields): compact_fields = compact_fields + ["image", "item_code", "item_name"] final_columns = [] @@ -26,3 +37,38 @@ def format_columns(display_columns, compact_fields): if column not in compact_fields: final_columns.append(column) return final_columns + +@frappe.whitelist() +def show_compact_item_setting(doc): + meta = frappe.get_meta(doc.doctype) + items_field = meta.get_field('items') + if items_field and items_field.fieldtype == 'Table': + return True + return False + +@frappe.whitelist() +def show_taxes_setting(doc): + meta = frappe.get_meta(doc.doctype) + items_field = meta.get_field('taxes') + if items_field and items_field.fieldtype == 'Table': + return True + return False + +def get_print_settings(): + settings = { + 'compact_item_print': { + 'condition': 'erpnext.controllers.print_settings.show_compact_item_setting', + 'fieldtype': 'Check', + 'child_field': 'items', + 'label': 'Compact Item Print', + 'set_template': 'erpnext.controllers.print_settings.print_settings_for_item_table' + }, + 'print_taxes_with_zero_amount': { + 'condition': 'erpnext.controllers.print_settings.show_taxes_setting', + 'fieldtype': 'Check', + 'label': 'Print taxes with zero amount', + 'set_template': 'erpnext.controllers.print_settings.print_settings_for_taxes' + } + } + + return settings diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 58861715c2..ac26d3fea8 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -11,20 +11,15 @@ from erpnext.stock.get_item_details import get_conversion_factor from erpnext.stock.doctype.item.item import set_item_default from frappe.contacts.doctype.address.address import get_address_display from erpnext.controllers.accounts_controller import get_taxes_and_charges +from erpnext.controllers.print_settings import print_settings_for_taxes from erpnext.controllers.stock_controller import StockController class SellingController(StockController): def __setup__(self): if hasattr(self, "taxes"): - self.flags.print_taxes_with_zero_amount = cint(frappe.db.get_single_value("Print Settings", - "print_taxes_with_zero_amount")) - self.flags.show_inclusive_tax_in_print = self.is_inclusive_tax() + print_settings_for_taxes(self) - self.print_templates = { - "total": "templates/print_formats/includes/total.html", - "taxes": "templates/print_formats/includes/taxes.html" - } def get_feed(self): return _("To {0} | {1} {2}").format(self.customer_name, self.currency, @@ -189,7 +184,7 @@ class SellingController(StockController): for it in self.get("items"): if not it.item_code: continue - + last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"]) last_purchase_rate_in_sales_uom = last_purchase_rate * (it.conversion_factor or 1) if flt(it.base_net_rate) < flt(last_purchase_rate_in_sales_uom): diff --git a/erpnext/hooks.py b/erpnext/hooks.py index b510208720..d455972a56 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -41,6 +41,7 @@ notification_config = "erpnext.startup.notifications.get_notification_config" get_help_messages = "erpnext.utilities.activation.get_help_messages" leaderboards = "erpnext.startup.leaderboard.get_leaderboards" filters_config = "erpnext.startup.filters.get_filters_config" +additional_print_settings = "erpnext.controllers.print_settings.get_print_settings" on_session_creation = [ "erpnext.portal.utils.create_customer_or_supplier", diff --git a/erpnext/startup/filters.py b/erpnext/startup/filters.py index a99e49b491..ec07329ded 100644 --- a/erpnext/startup/filters.py +++ b/erpnext/startup/filters.py @@ -2,13 +2,13 @@ import frappe def get_filters_config(): - filters_config = { + filters_config = { "fiscal year": { "label": "Fiscal Year", "get_field": "erpnext.accounts.utils.get_fiscal_year_filter_field", "valid_for_fieldtypes": ["Date", "Datetime", "DateRange"], "depends_on": "company", } - } + } - return filters_config \ No newline at end of file + return filters_config \ No newline at end of file From 13c6e2f320c100de245a01b18ddf6a134bbed8b5 Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 22 Oct 2020 17:19:00 +0530 Subject: [PATCH 048/728] feat: contextual setting for print uom after quantity --- erpnext/controllers/print_settings.py | 46 +++++++++++++------ .../includes/item_table_qty.html | 2 +- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/erpnext/controllers/print_settings.py b/erpnext/controllers/print_settings.py index edd722dbe8..5c29556398 100644 --- a/erpnext/controllers/print_settings.py +++ b/erpnext/controllers/print_settings.py @@ -5,24 +5,24 @@ from __future__ import unicode_literals import frappe from frappe.utils import cint -def print_settings_for_item_table(doc, setting_value=None): - +def print_settings_for_item_table(doc, settings=None): doc.print_templates = { "qty": "templates/print_formats/includes/item_table_qty.html" } doc.hide_in_print_layout = ["uom", "stock_uom"] - doc.flags.compact_item_print = setting_value if setting_value is not None\ - else cint(frappe.db.get_single_value("Print Settings", "compact_item_print")) + setting_fields = ['compact_item_print', 'print_uom_after_quantity'] + set_doc_flags_from_settings(doc, setting_fields, settings) if doc.flags.compact_item_print: doc.print_templates["description"] = "templates/print_formats/includes/item_table_description.html" doc.flags.compact_item_fields = ["description", "qty", "rate", "amount"] doc.flags.format_columns = format_columns -def print_settings_for_taxes(doc, setting_value=None): - doc.flags.print_taxes_with_zero_amount = setting_value if setting_value is not None\ - else cint(frappe.db.get_single_value("Print Settings", "print_taxes_with_zero_amount")) +def print_settings_for_taxes(doc, settings=None): + + set_doc_flags_from_settings(doc, ['print_taxes_with_zero_amount'], settings) + doc.flags.show_inclusive_tax_in_print = doc.is_inclusive_tax() doc.print_templates = { @@ -30,6 +30,17 @@ def print_settings_for_taxes(doc, setting_value=None): "taxes": "templates/print_formats/includes/taxes.html" } +def set_doc_flags_from_settings(doc, fields, settings=None): + if not settings: settings = {} + + print_settings = frappe.get_single('Print Settings') + + for field in fields: + if field in settings: + doc.flags[field] = settings.get(field) + else: + doc.flags[field] = print_settings.get(field) + def format_columns(display_columns, compact_fields): compact_fields = compact_fields + ["image", "item_code", "item_name"] final_columns = [] @@ -38,33 +49,38 @@ def format_columns(display_columns, compact_fields): final_columns.append(column) return final_columns -@frappe.whitelist() -def show_compact_item_setting(doc): +def has_items_field(doc): meta = frappe.get_meta(doc.doctype) items_field = meta.get_field('items') if items_field and items_field.fieldtype == 'Table': return True return False -@frappe.whitelist() -def show_taxes_setting(doc): +def has_taxes_field(doc): meta = frappe.get_meta(doc.doctype) - items_field = meta.get_field('taxes') - if items_field and items_field.fieldtype == 'Table': + taxes_field = meta.get_field('taxes') + if taxes_field and taxes_field.fieldtype == 'Table': return True return False def get_print_settings(): settings = { 'compact_item_print': { - 'condition': 'erpnext.controllers.print_settings.show_compact_item_setting', + 'condition': 'erpnext.controllers.print_settings.has_items_field', 'fieldtype': 'Check', 'child_field': 'items', 'label': 'Compact Item Print', 'set_template': 'erpnext.controllers.print_settings.print_settings_for_item_table' }, + 'print_uom_after_quantity': { + 'condition': 'erpnext.controllers.print_settings.has_taxes_field', + 'fieldtype': 'Check', + 'child_field': 'items', + 'label': 'Print UOM after Quantity', + 'set_template': 'erpnext.controllers.print_settings.print_settings_for_item_table' + }, 'print_taxes_with_zero_amount': { - 'condition': 'erpnext.controllers.print_settings.show_taxes_setting', + 'condition': 'erpnext.controllers.print_settings.has_taxes_field', 'fieldtype': 'Check', 'label': 'Print taxes with zero amount', 'set_template': 'erpnext.controllers.print_settings.print_settings_for_taxes' diff --git a/erpnext/templates/print_formats/includes/item_table_qty.html b/erpnext/templates/print_formats/includes/item_table_qty.html index ecaaef42b5..1d992dc302 100644 --- a/erpnext/templates/print_formats/includes/item_table_qty.html +++ b/erpnext/templates/print_formats/includes/item_table_qty.html @@ -1,4 +1,4 @@ -{% set qty_first=frappe.db.get_single_value("Print Settings", "print_uom_after_quantity") %} +{% set qty_first=doc.flags.print_uom_after_quantity %} {% if qty_first %} {{ doc.get_formatted("qty", doc) }} {% if (doc.uom and not doc.is_print_hide("uom")) %} {{ _(doc.uom) }} From bc6a2b2f9c260b2073ed2195d3147289664389f1 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 27 Oct 2020 19:42:58 +0530 Subject: [PATCH 049/728] wip --- erpnext/controllers/buying_controller.py | 8 +++++ erpnext/controllers/print_settings.py | 14 +++----- erpnext/controllers/selling_controller.py | 11 +++++++ .../print_formats/includes/items.html | 33 +++++++++++++++++++ 4 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 erpnext/templates/print_formats/includes/items.html diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index de04abbca4..596f6f4b57 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -23,6 +23,14 @@ class BuyingController(StockController): if hasattr(self, "taxes"): print_settings_for_taxes(self) + def before_print(self): + pass + + def get_print_settings(self): + items_field = self.meta.get_field('items') + if items_field and items_field.fieldtype == 'Table': + return ['compact_item_print', 'print_uom_after_quantity'] + def get_feed(self): if self.get("supplier_name"): return _("From {0} | {1} {2}").format(self.supplier_name, self.currency, diff --git a/erpnext/controllers/print_settings.py b/erpnext/controllers/print_settings.py index 5c29556398..418d8eb328 100644 --- a/erpnext/controllers/print_settings.py +++ b/erpnext/controllers/print_settings.py @@ -64,26 +64,22 @@ def has_taxes_field(doc): return False def get_print_settings(): + + # return ['comp'] settings = { 'compact_item_print': { 'condition': 'erpnext.controllers.print_settings.has_items_field', - 'fieldtype': 'Check', 'child_field': 'items', - 'label': 'Compact Item Print', - 'set_template': 'erpnext.controllers.print_settings.print_settings_for_item_table' + # 'set_template': 'erpnext.controllers.print_settings.print_settings_for_item_table' }, 'print_uom_after_quantity': { 'condition': 'erpnext.controllers.print_settings.has_taxes_field', - 'fieldtype': 'Check', 'child_field': 'items', - 'label': 'Print UOM after Quantity', - 'set_template': 'erpnext.controllers.print_settings.print_settings_for_item_table' + # 'set_template': 'erpnext.controllers.print_settings.print_settings_for_item_table' }, 'print_taxes_with_zero_amount': { 'condition': 'erpnext.controllers.print_settings.has_taxes_field', - 'fieldtype': 'Check', - 'label': 'Print taxes with zero amount', - 'set_template': 'erpnext.controllers.print_settings.print_settings_for_taxes' + # 'set_template': 'erpnext.controllers.print_settings.print_settings_for_taxes' } } diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index ac26d3fea8..4c87e8c2f6 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -20,6 +20,17 @@ class SellingController(StockController): if hasattr(self, "taxes"): print_settings_for_taxes(self) + def before_print(self): + self.print_templates = { + "items": "templates/print_formats/includes/items.html", + } + self.flags.compact_item_fields = ['description'] + + def get_print_settings(self): + items_field = self.meta.get_field('items') + if items_field and items_field.fieldtype == 'Table': + return ['compact_item_print', 'print_uom_after_quantity'] + def get_feed(self): return _("To {0} | {1} {2}").format(self.customer_name, self.currency, diff --git a/erpnext/templates/print_formats/includes/items.html b/erpnext/templates/print_formats/includes/items.html new file mode 100644 index 0000000000..933ae17c2a --- /dev/null +++ b/erpnext/templates/print_formats/includes/items.html @@ -0,0 +1,33 @@ +{%- if data -%} + {%- set visible_columns = get_visible_columns(doc.get(df.fieldname), + table_meta, df) -%} + +
+
+ + + + {% for tdf in visible_columns %} + {% if (data and not print_settings.compact_item_print) or tdf.fieldname in doc.get(df.fieldname)[0].flags.compact_item_fields %} + + {% endif %} + {% endfor %} + + + + {% for d in data %} + + + {% for tdf in visible_columns %} + {% if not doc.get(df.fieldname)[0].flags.compact_item_print or tdf.fieldname in doc.get(df.fieldname)[0].flags.compact_item_fields %} + + {% endif %} + {% endfor %} + + {% endfor %} + +
{{ _("Sr") }} + {{ _(tdf.label) }}
{{ d.idx }} +
{{ print_value(tdf, d, doc, visible_columns) }}
+
+{%- endif -%} From 71e5b60df11035b21aba47b08c955bd9f30cf158 Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 29 Oct 2020 14:19:34 +0530 Subject: [PATCH 050/728] refactor: refactor contextual print setting implementation --- .../purchase_invoice_item.py | 5 +- .../sales_invoice_item/sales_invoice_item.py | 4 +- .../sales_invoice_return.html | 11 ++- .../purchase_order_item.py | 5 +- .../supplier_quotation_item.py | 5 +- erpnext/controllers/accounts_controller.py | 19 ++++- erpnext/controllers/buying_controller.py | 12 --- erpnext/controllers/print_settings.py | 80 +++++-------------- erpnext/controllers/selling_controller.py | 16 ---- erpnext/projects/doctype/project/project.py | 2 +- .../doctype/quotation_item/quotation_item.py | 4 +- .../sales_order_item/sales_order_item.py | 4 +- .../doctype/delivery_note/delivery_note.py | 2 +- .../delivery_note_item/delivery_note_item.py | 4 +- .../material_request_item.py | 4 +- .../purchase_receipt_item.py | 4 +- .../includes/item_table_description.html | 6 +- .../includes/item_table_qty.html | 2 +- .../print_formats/includes/items.html | 12 ++- .../print_formats/includes/taxes.html | 2 +- 20 files changed, 68 insertions(+), 135 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py index 56576df079..50ec7d8b4d 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py @@ -6,8 +6,5 @@ import frappe from frappe.model.document import Document -from erpnext.controllers.print_settings import print_settings_for_item_table - class PurchaseInvoiceItem(Document): - def __setup__(self): - print_settings_for_item_table(self) + pass diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py index 7a62f8e281..a73b03acc8 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py @@ -5,8 +5,6 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -from erpnext.controllers.print_settings import print_settings_for_item_table class SalesInvoiceItem(Document): - def __setup__(self): - print_settings_for_item_table(self) + pass diff --git a/erpnext/accounts/print_format/sales_invoice_return/sales_invoice_return.html b/erpnext/accounts/print_format/sales_invoice_return/sales_invoice_return.html index 1d758e8935..3d5a9b1da9 100644 --- a/erpnext/accounts/print_format/sales_invoice_return/sales_invoice_return.html +++ b/erpnext/accounts/print_format/sales_invoice_return/sales_invoice_return.html @@ -1,5 +1,5 @@ {%- from "templates/print_formats/standard_macros.html" import add_header, render_field, print_value, fieldmeta, - get_width, get_align_class -%} + get_width, get_align_class with context -%} {%- macro render_currency(df, doc) -%}
@@ -63,14 +63,19 @@ {{ d.idx }} {% for tdf in visible_columns %} - {% if not d.flags.compact_item_print or tdf.fieldname in doc.get(df.fieldname)[0].flags.compact_item_fields %} + {% if not print_settings.compact_item_print or tdf.fieldname in doc.flags.compact_item_fields %} {% if tdf.fieldname == 'qty' %}
{{ (d[tdf.fieldname])|abs }}
{% elif tdf.fieldtype == 'Currency' %}
{{ frappe.utils.fmt_money((d[tdf.fieldname])|abs, currency=doc.currency) }}
{% else %} -
{{ print_value(tdf, d, doc, visible_columns) }}
+ {% if doc.child_print_templates %} + {%- set child_templates = doc.child_print_templates.get(df.fieldname) -%} +
{{ print_value(tdf, d, doc, visible_columns, child_templates) }}
+ {% else %} +
{{ print_value(tdf, d, doc, visible_columns) }}
+ {% endif %} {% endif %} {% endif %} {% endfor %} diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py index b711e36bf9..8bdcd47e02 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py @@ -6,11 +6,8 @@ import frappe from frappe.model.document import Document -from erpnext.controllers.print_settings import print_settings_for_item_table - class PurchaseOrderItem(Document): - def __setup__(self): - print_settings_for_item_table(self) + pass def on_doctype_update(): frappe.db.add_index("Purchase Order Item", ["item_code", "warehouse"]) \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.py b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.py index f24e5be076..64dda87945 100644 --- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.py +++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.py @@ -6,8 +6,5 @@ import frappe from frappe.model.document import Document -from erpnext.controllers.print_settings import print_settings_for_item_table - class SupplierQuotationItem(Document): - def __setup__(self): - print_settings_for_item_table(self) + pass diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 166564961d..c016090314 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -22,6 +22,7 @@ from six import text_type from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions 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 +from erpnext.controllers.print_settings import set_print_templates_for_item_table, set_print_templates_for_taxes force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules") @@ -29,6 +30,19 @@ class AccountsController(TransactionBase): def __init__(self, *args, **kwargs): super(AccountsController, self).__init__(*args, **kwargs) + def get_print_settings(self): + print_setting_fields = [] + items_field = self.meta.get_field('items') + + if items_field and items_field.fieldtype == 'Table': + print_setting_fields += ['compact_item_print', 'print_uom_after_quantity'] + + taxes_field = self.meta.get_field('taxes') + if taxes_field and taxes_field.fieldtype == 'Table': + print_setting_fields += ['print_taxes_with_zero_amount'] + + return print_setting_fields + @property def company_currency(self): if not hasattr(self, "__company_currency"): @@ -138,7 +152,7 @@ class AccountsController(TransactionBase): elif self.doctype in ("Quotation", "Purchase Order", "Sales Order"): self.validate_non_invoice_documents_schedule() - def before_print(self): + def before_print(self, settings=None): if self.doctype in ['Purchase Order', 'Sales Order', 'Sales Invoice', 'Purchase Invoice', 'Supplier Quotation', 'Purchase Receipt', 'Delivery Note', 'Quotation']: if self.get("group_same_items"): @@ -151,6 +165,9 @@ class AccountsController(TransactionBase): else: df.set("print_hide", 1) + set_print_templates_for_item_table(self, settings) + set_print_templates_for_taxes(self, settings) + def calculate_paid_amount(self): if hasattr(self, "is_pos") or hasattr(self, "is_paid"): is_paid = self.get("is_pos") or self.get("is_paid") diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 596f6f4b57..353bc09810 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -13,23 +13,11 @@ from erpnext.stock.stock_ledger import get_valuation_rate from erpnext.stock.doctype.stock_entry.stock_entry import get_used_alternative_items from erpnext.stock.doctype.serial_no.serial_no import get_auto_serial_nos, auto_make_serial_nos, get_serial_nos from frappe.contacts.doctype.address.address import get_address_display -from erpnext.controllers.print_settings import print_settings_for_taxes from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget from erpnext.controllers.stock_controller import StockController class BuyingController(StockController): - def __setup__(self): - if hasattr(self, "taxes"): - print_settings_for_taxes(self) - - def before_print(self): - pass - - def get_print_settings(self): - items_field = self.meta.get_field('items') - if items_field and items_field.fieldtype == 'Table': - return ['compact_item_print', 'print_uom_after_quantity'] def get_feed(self): if self.get("supplier_name"): diff --git a/erpnext/controllers/print_settings.py b/erpnext/controllers/print_settings.py index 418d8eb328..e08c400068 100644 --- a/erpnext/controllers/print_settings.py +++ b/erpnext/controllers/print_settings.py @@ -5,41 +5,33 @@ from __future__ import unicode_literals import frappe from frappe.utils import cint -def print_settings_for_item_table(doc, settings=None): +def set_print_templates_for_item_table(doc, settings): doc.print_templates = { - "qty": "templates/print_formats/includes/item_table_qty.html" + "items": "templates/print_formats/includes/items.html", } - doc.hide_in_print_layout = ["uom", "stock_uom"] - setting_fields = ['compact_item_print', 'print_uom_after_quantity'] - set_doc_flags_from_settings(doc, setting_fields, settings) + doc.child_print_templates = { + "items": { + "qty": "templates/print_formats/includes/item_table_qty.html", + } + } - if doc.flags.compact_item_print: - doc.print_templates["description"] = "templates/print_formats/includes/item_table_description.html" - doc.flags.compact_item_fields = ["description", "qty", "rate", "amount"] + if doc.meta.get_field("items"): + doc.meta.get_field("items").hide_in_print_layout = ["uom", "stock_uom"] + + doc.flags.compact_item_fields = ["description", "qty", "rate", "amount"] + + if settings.compact_item_print: + doc.child_print_templates["items"]["description"] =\ + "templates/print_formats/includes/item_table_description.html" doc.flags.format_columns = format_columns -def print_settings_for_taxes(doc, settings=None): - - set_doc_flags_from_settings(doc, ['print_taxes_with_zero_amount'], settings) - +def set_print_templates_for_taxes(doc, settings): doc.flags.show_inclusive_tax_in_print = doc.is_inclusive_tax() - - doc.print_templates = { + doc.print_templates.update({ "total": "templates/print_formats/includes/total.html", "taxes": "templates/print_formats/includes/taxes.html" - } - -def set_doc_flags_from_settings(doc, fields, settings=None): - if not settings: settings = {} - - print_settings = frappe.get_single('Print Settings') - - for field in fields: - if field in settings: - doc.flags[field] = settings.get(field) - else: - doc.flags[field] = print_settings.get(field) + }) def format_columns(display_columns, compact_fields): compact_fields = compact_fields + ["image", "item_code", "item_name"] @@ -48,39 +40,3 @@ def format_columns(display_columns, compact_fields): if column not in compact_fields: final_columns.append(column) return final_columns - -def has_items_field(doc): - meta = frappe.get_meta(doc.doctype) - items_field = meta.get_field('items') - if items_field and items_field.fieldtype == 'Table': - return True - return False - -def has_taxes_field(doc): - meta = frappe.get_meta(doc.doctype) - taxes_field = meta.get_field('taxes') - if taxes_field and taxes_field.fieldtype == 'Table': - return True - return False - -def get_print_settings(): - - # return ['comp'] - settings = { - 'compact_item_print': { - 'condition': 'erpnext.controllers.print_settings.has_items_field', - 'child_field': 'items', - # 'set_template': 'erpnext.controllers.print_settings.print_settings_for_item_table' - }, - 'print_uom_after_quantity': { - 'condition': 'erpnext.controllers.print_settings.has_taxes_field', - 'child_field': 'items', - # 'set_template': 'erpnext.controllers.print_settings.print_settings_for_item_table' - }, - 'print_taxes_with_zero_amount': { - 'condition': 'erpnext.controllers.print_settings.has_taxes_field', - # 'set_template': 'erpnext.controllers.print_settings.print_settings_for_taxes' - } - } - - return settings diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 4c87e8c2f6..de3cda540a 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -11,26 +11,10 @@ from erpnext.stock.get_item_details import get_conversion_factor from erpnext.stock.doctype.item.item import set_item_default from frappe.contacts.doctype.address.address import get_address_display from erpnext.controllers.accounts_controller import get_taxes_and_charges -from erpnext.controllers.print_settings import print_settings_for_taxes from erpnext.controllers.stock_controller import StockController class SellingController(StockController): - def __setup__(self): - if hasattr(self, "taxes"): - print_settings_for_taxes(self) - - def before_print(self): - self.print_templates = { - "items": "templates/print_formats/includes/items.html", - } - self.flags.compact_item_fields = ['description'] - - def get_print_settings(self): - items_field = self.meta.get_field('items') - if items_field and items_field.fieldtype == 'Table': - return ['compact_item_print', 'print_uom_after_quantity'] - def get_feed(self): return _("To {0} | {1} {2}").format(self.customer_name, self.currency, diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 5bbd29c4c4..24d0dc16bf 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -26,7 +26,7 @@ class Project(Document): self.update_costing() - def before_print(self): + def before_print(self, settings=None): self.onload() diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.py b/erpnext/selling/doctype/quotation_item/quotation_item.py index 966b542c41..7384871ed4 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.py +++ b/erpnext/selling/doctype/quotation_item/quotation_item.py @@ -5,8 +5,6 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -from erpnext.controllers.print_settings import print_settings_for_item_table class QuotationItem(Document): - def __setup__(self): - print_settings_for_item_table(self) + pass diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.py b/erpnext/selling/doctype/sales_order_item/sales_order_item.py index 4a87a0c28a..27f303d43b 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.py +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.py @@ -5,11 +5,9 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -from erpnext.controllers.print_settings import print_settings_for_item_table class SalesOrderItem(Document): - def __setup__(self): - print_settings_for_item_table(self) + pass def on_doctype_update(): frappe.db.add_index("Sales Order Item", ["item_code", "warehouse"]) \ No newline at end of file diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index d04cf785ab..1994dfd204 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -71,7 +71,7 @@ class DeliveryNote(SellingController): where name=`tabSales Invoice Item`.parent and is_return=1 and update_stock=1)""" }) - def before_print(self): + def before_print(self, settings=None): def toggle_print_hide(meta, fieldname): df = meta.get_field(fieldname) if self.get("print_without_amount"): diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py index aaca802f5c..5030595789 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py @@ -5,8 +5,6 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -from erpnext.controllers.print_settings import print_settings_for_item_table class DeliveryNoteItem(Document): - def __setup__(self): - print_settings_for_item_table(self) + pass \ No newline at end of file diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.py b/erpnext/stock/doctype/material_request_item/material_request_item.py index 6c6ecfea83..16f007f6a2 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.py +++ b/erpnext/stock/doctype/material_request_item/material_request_item.py @@ -6,12 +6,10 @@ from __future__ import unicode_literals import frappe -from erpnext.controllers.print_settings import print_settings_for_item_table from frappe.model.document import Document class MaterialRequestItem(Document): - def __setup__(self): - print_settings_for_item_table(self) + pass def on_doctype_update(): frappe.db.add_index("Material Request Item", ["item_code", "warehouse"]) \ No newline at end of file diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py index 679bd1e242..b79bb5d043 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py @@ -5,8 +5,6 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -from erpnext.controllers.print_settings import print_settings_for_item_table class PurchaseReceiptItem(Document): - def __setup__(self): - print_settings_for_item_table(self) + pass diff --git a/erpnext/templates/print_formats/includes/item_table_description.html b/erpnext/templates/print_formats/includes/item_table_description.html index 070cca5eb2..ed46c9e224 100644 --- a/erpnext/templates/print_formats/includes/item_table_description.html +++ b/erpnext/templates/print_formats/includes/item_table_description.html @@ -1,7 +1,7 @@ -{%- set compact = doc.flags.compact_item_print -%} -{%- set compact_fields = doc.flags.compact_item_fields -%} +{%- set compact = print_settings.compact_item_print -%} +{%- set compact_fields = parent_doc.flags.compact_item_fields -%} {%- set display_columns = visible_columns|map(attribute="fieldname")| list -%} -{%- set columns = doc.flags.format_columns(display_columns, compact_fields) -%} +{%- set columns = parent_doc.flags.format_columns(display_columns, compact_fields) -%} {% if doc.in_format_data("image") and doc.get("image") and "image" in display_columns -%}
diff --git a/erpnext/templates/print_formats/includes/item_table_qty.html b/erpnext/templates/print_formats/includes/item_table_qty.html index 1d992dc302..8e68f1cc63 100644 --- a/erpnext/templates/print_formats/includes/item_table_qty.html +++ b/erpnext/templates/print_formats/includes/item_table_qty.html @@ -1,4 +1,4 @@ -{% set qty_first=doc.flags.print_uom_after_quantity %} +{% set qty_first=print_settings.print_uom_after_quantity %} {% if qty_first %} {{ doc.get_formatted("qty", doc) }} {% if (doc.uom and not doc.is_print_hide("uom")) %} {{ _(doc.uom) }} diff --git a/erpnext/templates/print_formats/includes/items.html b/erpnext/templates/print_formats/includes/items.html index 933ae17c2a..55598e7e22 100644 --- a/erpnext/templates/print_formats/includes/items.html +++ b/erpnext/templates/print_formats/includes/items.html @@ -1,14 +1,13 @@ {%- if data -%} {%- set visible_columns = get_visible_columns(doc.get(df.fieldname), table_meta, df) -%} -
{% for tdf in visible_columns %} - {% if (data and not print_settings.compact_item_print) or tdf.fieldname in doc.get(df.fieldname)[0].flags.compact_item_fields %} + {% if (data and not print_settings.compact_item_print) or tdf.fieldname in doc.flags.compact_item_fields %} {% endif %} @@ -20,9 +19,14 @@ {% for tdf in visible_columns %} - {% if not doc.get(df.fieldname)[0].flags.compact_item_print or tdf.fieldname in doc.get(df.fieldname)[0].flags.compact_item_fields %} + {% if not print_settings.compact_item_print or tdf.fieldname in doc.flags.compact_item_fields %} + {% if doc.child_print_templates %} + {%- set child_templates = doc.child_print_templates.get(df.fieldname) %} +
{{ print_value(tdf, d, doc, visible_columns, child_templates) }}
+ {% else %} +
{{ print_value(tdf, d, doc, visible_columns) }}
+ {% endif %} {% endif %} {% endfor %} diff --git a/erpnext/templates/print_formats/includes/taxes.html b/erpnext/templates/print_formats/includes/taxes.html index 6e984f3901..334ac7834e 100644 --- a/erpnext/templates/print_formats/includes/taxes.html +++ b/erpnext/templates/print_formats/includes/taxes.html @@ -17,7 +17,7 @@ {{ render_discount_amount(doc) }} {%- endif -%} {%- for charge in data -%} - {%- 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) -%} + {%- if (charge.tax_amount or print_settings.print_taxes_with_zero_amount) and (not charge.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}
From 18ef17f3ba5760569ea650288c45c197abc9f7dd Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 30 Oct 2020 15:41:36 +0530 Subject: [PATCH 051/728] feat: add tour for sales taxes template --- .../sales_taxes_and_charges_template.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js index 97a6fdd336..0e011883b1 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js @@ -5,3 +5,25 @@ cur_frm.cscript.tax_table = "Sales Taxes and Charges"; {% include "erpnext/public/js/controllers/accounts.js" %} +frappe.tour['Sales Taxes and Charges Template'] = [ + { + fieldname: "title", + title: __("Title"), + description: __("A name by which you will identify this template. You can change this later."), + }, + { + fieldname: "company", + title: __("Company"), + description: __("Company for which this tax template will be applicable"), + }, + { + fieldname: "is_default", + title: __("Is this Default?"), + description: __("Set this template as the default for all sales transactions"), + }, + { + fieldname: "taxes", + title: __("Taxes Table"), + description: __("You can add a row for a tax rule here. These rules can be applied on the net total, or can be a flat amount."), + } +]; From 2d1f8341f97fe9cf5849b86b0e187e6b43944870 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 30 Oct 2020 15:41:58 +0530 Subject: [PATCH 052/728] feat: update onboarding for accounts --- .../accounts/module_onboarding/accounts/accounts.json | 2 +- .../chart_of_accounts/chart_of_accounts.json | 7 ++++--- .../configure_account_settings.json | 2 +- .../create_a_customer/create_a_customer.json | 7 +++---- .../create_a_product/create_a_product.json | 9 +++++---- .../create_a_supplier/create_a_supplier.json | 6 +++--- .../create_your_first_purchase_invoice.json | 6 +++--- .../create_your_first_sales_invoice.json | 2 +- .../onboarding_step/setup_taxes/setup_taxes.json | 7 ++++--- 9 files changed, 25 insertions(+), 23 deletions(-) diff --git a/erpnext/accounts/module_onboarding/accounts/accounts.json b/erpnext/accounts/module_onboarding/accounts/accounts.json index 83748dd972..6b5c5a1db8 100644 --- a/erpnext/accounts/module_onboarding/accounts/accounts.json +++ b/erpnext/accounts/module_onboarding/accounts/accounts.json @@ -13,7 +13,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts", "idx": 0, "is_complete": 0, - "modified": "2020-10-19 14:43:45.080823", + "modified": "2020-10-30 15:41:15.547225", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts", diff --git a/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json index ff9dfd2d73..fc49bd652b 100644 --- a/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json +++ b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json @@ -1,23 +1,24 @@ { "action": "Go to Page", + "action_label": "View Chart of Accounts", "callback_message": "You can continue with the onboarding after exploring this page", "callback_title": "Awesome Work", "creation": "2020-05-13 19:58:20.928127", - "description": "# Chart Of Accounts\n\n**The Chart of Accounts is the blueprint of the accounts in your organization.**\n\nThe overall structure of your Chart of Accounts is based on a system of double entry\naccounting that has become a standard all over the world to quantify how a\ncompany is doing financially.\n\nChart of Accounts is a tree view of the names of the Accounts (Ledgers and\nGroups) that a Company requires to manage its books of accounts. ERPNext sets\nup a simple chart of accounts for each Company you create, but you can\nmodify it according to your needs and legal requirements.\n\nFor each company, Chart of Accounts signifies the way to classify the accounting entries, mostly\nbased on statutory (tax, compliance to government regulations) requirements.\n\nThe Chart of Accounts helps you to answer questions like:\n\n * What is your organization worth?\n * How much debt have you taken?\n * How much profit are you making (and hence paying tax)?\n * How much are you selling?\n * What is your expense break-up?\n", + "description": "# Chart Of Accounts\n\nThe Chart of Accounts is the blueprint of the accounts in your organization.\nIt is a tree view of the names of the Accounts (Ledgers and Groups) that a Company requires to manage its books of accounts. ERPNext sets up a simple chart of accounts for each Company you create, but you can modify it according to your needs and legal requirements.\n\nFor each company, Chart of Accounts signifies the way to classify the accounting entries, mostly\nbased on statutory (tax, compliance to government regulations) requirements.\n\nThere's a brief video tutorial about chart of accounts in the next step.", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, "intro_video_url": "https://www.youtube.com/embed/AcfMCT7wLLo", "is_complete": 0, - "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-10-19 14:25:31.427339", + "modified": "2020-10-30 14:35:59.474920", "modified_by": "Administrator", "name": "Chart of Accounts", "owner": "Administrator", "path": "Tree/Account", "reference_document": "Account", + "show_form_tour": 0, "show_full_form": 0, "title": "Review Chart of Accounts", "validate_action": 0 diff --git a/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json b/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json index 083fbb52bf..c84430a0c6 100644 --- a/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json +++ b/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json @@ -6,7 +6,6 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, "is_single": 1, "is_skipped": 0, "modified": "2020-10-19 14:40:55.584484", @@ -14,6 +13,7 @@ "name": "Configure Account Settings", "owner": "Administrator", "reference_document": "Accounts Settings", + "show_form_tour": 0, "show_full_form": 1, "title": "Configure Account Settings", "validate_action": 1 diff --git a/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json index 4a967b140b..0b6750c5f8 100644 --- a/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json +++ b/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json @@ -1,20 +1,19 @@ { "action": "Create Entry", "creation": "2020-05-14 17:46:41.831517", - "description": "# Customer\n\nA customer, who is sometimes known as a client, buyer, or purchaser is the one who receives goods, services, products, or ideas, from a seller for a monetary consideration.\n\n### Creating a customer is easy and can be done in the following steps\n\n1. Go to the Customer list and click on New.\n2. Enter Full Name of the customer.\n3. Select **Company** if the customer represents a company or **Individual** otherwise in Type field.\n4. Select a Customer Group. A few groups are included by default, you can create additional groups if you need.\n5. Select the Territory.\n6. If the customer is being created against a lead, you can select the same in From Lead field.\n7. Save.\n\nUp next is a video about customers and suppliers that will give you more clarity on these concepts in ERPNext", + "description": "## Who is a Customer?\n\nA customer, who is sometimes known as a client, buyer, or purchaser is the one who receives goods, services, products, or ideas, from a seller for a monetary consideration.\n\nEvery customer needs to be assigned a unique id. Customer name itself can be the id or you can set a naming series for ids to be generated in Selling Settings.\n\nJust like the supplier, let's quickly create a customer.", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, - "intro_video_url": "https://www.youtube.com/watch?v=zsrrVDk6VBs", "is_complete": 0, - "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-10-16 12:59:16.989156", + "modified": "2020-10-30 15:28:46.659660", "modified_by": "Administrator", "name": "Create a Customer", "owner": "Administrator", "reference_document": "Customer", + "show_form_tour": 0, "show_full_form": 0, "title": "Create a Customer", "validate_action": 1 diff --git a/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json b/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json index 0cbefee858..d76f645d96 100644 --- a/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json +++ b/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json @@ -1,20 +1,21 @@ { "action": "Create Entry", "creation": "2020-05-12 18:16:06.624554", - "description": "## Creating Products\n\nIn ERPNext, any product or a service offered by your company is called an Item. The term Item is also applicable to raw materials or components of products yet to be produced (before they can be sold to customers). ERPNext allows you to manage all sorts of items like raw-materials, sub-assemblies, finished goods, item variants, and service items.\n\nERPNext is optimized for itemized management of your sales and purchase. If you are in services, you can create an Item for each service that you offer. Completing the Item Master is very essential for the successful implementation of ERPNext.\n\nYou can access the Item section\n\n`Desk > Stock > Item`\n\n\n", + "description": "## Products and Services\n\nDepending on the nature of your business, you might be selling products or services to your clients or even both. \nERPNext is optimized for itemized management of your sales and purchase.\n\nThe **Item Master** is where you can add all your sales items. If you are in services, you can create an Item for each service that you offer. If you run a manufacturing business, the same master is used for keeping a record of raw materials, sub-assemblies etc.\n\nCompleting the Item Master is very essential for the successful implementation of ERPNext. We have a brief video introducing the item master for you, you can watch it in the next step.", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, + "intro_video_url": "https://www.youtube.com/watch?v=Sl5UFA5H5EQ", "is_complete": 0, - "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-10-16 12:59:16.983833", + "modified": "2020-10-30 15:20:30.133495", "modified_by": "Administrator", "name": "Create a Product", "owner": "Administrator", "reference_document": "Item", + "show_form_tour": 0, "show_full_form": 0, - "title": "Create a Product", + "title": "Create a Sales Item", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json index eede11d979..64bc7bbfef 100644 --- a/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json @@ -1,19 +1,19 @@ { "action": "Create Entry", "creation": "2020-05-14 22:09:10.043554", - "description": "## Let's add your Suppliers\n\nSuppliers are companies or individuals who provide you with products or services. A supplier may be distinguished from a contractor or subcontractor, who commonly adds specialized input to deliverables. A supplier is also known as a vendor. There are different types of suppliers based on the goods and products they supply.\n\nERPNext allows you to create your own categories of suppliers. These categories are known as Supplier Groups. For example, if your suppliers are mainly pharmaceutical companies and FMCG distributors, you can create a new Supplier Groups for them and name the groups accordingly.", + "description": "## Who is a Supplier?\n\nSuppliers are companies or individuals who provide you with products or services. ERPNext has comprehensive features for purchase cycles. \n\nLet's quickly create a supplier with the minimal details required. You need the name of the supplier, assign the supplier to a group, and select the type of the supplier, viz. Company or Individual.", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-10-16 12:59:16.979176", + "modified": "2020-10-30 15:26:48.315772", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", "reference_document": "Supplier", + "show_form_tour": 0, "show_full_form": 0, "title": "Create a Supplier", "validate_action": 1 diff --git a/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json index 2b53f46fe9..ddbc89ec0a 100644 --- a/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json +++ b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json @@ -1,19 +1,19 @@ { "action": "Create Entry", "creation": "2020-05-14 22:10:07.049704", - "description": "# What's a Purchase Invoice?\n\nA Purchase Invoice is a bill you receive from your Suppliers against which you need to make the payment.\nPurchase Invoice is the exact opposite of your Sales Invoice. Here you accrue expenses to your Supplier. Making a Purchase Invoice is very similar to making a Purchase Order.\n\n![Purchase Flow](https://docs.erpnext.com/docs/assets/img/accounts/pi-flow.png)\n\n", + "description": "# What's a Purchase Invoice?\n\nA Purchase Invoice is a bill you receive from your Suppliers against which you need to make the payment.\nPurchase Invoice is the exact opposite of your Sales Invoice. Here you accrue expenses to your Supplier. \n\nThe following is what a typical purchase cycle looks like, however you can create a purchase invoice directly as well.\n\n![Purchase Flow](https://docs.erpnext.com/docs/assets/img/accounts/pi-flow.png)\n\n", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-10-16 12:59:16.976334", + "modified": "2020-10-30 15:30:26.337773", "modified_by": "Administrator", "name": "Create Your First Purchase Invoice", "owner": "Administrator", "reference_document": "Purchase Invoice", + "show_form_tour": 0, "show_full_form": 1, "title": "Create Your First Purchase Invoice ", "validate_action": 1 diff --git a/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json b/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json index d56be9220d..9e7dd67900 100644 --- a/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json +++ b/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json @@ -6,7 +6,6 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, "is_single": 0, "is_skipped": 0, "modified": "2020-10-16 12:59:16.987507", @@ -14,6 +13,7 @@ "name": "Create Your First Sales Invoice", "owner": "Administrator", "reference_document": "Sales Invoice", + "show_form_tour": 0, "show_full_form": 1, "title": "Create Your First Sales Invoice ", "validate_action": 1 diff --git a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json index fa3eabd974..a4922013da 100644 --- a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json +++ b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json @@ -1,19 +1,20 @@ { "action": "Create Entry", + "action_label": "Make a Sales Tax Template", "creation": "2020-05-13 19:29:43.844463", - "description": "# Setting up Taxes\n\nOne of the primary motivators for compulsory use of accounting tools is the calculation of taxes. ERPNext allows you to make configurable tax templates that you can apply to your sales or purchase transactions.\n\nThe templates created from this form can be used in Sales Orders and Sales Invoices. The way ERPNext sets up taxes is via templates. Other types of charges that may apply to your invoices (like shipping, insurance etc.) can also be configured as taxes.\n\nFor Tax Accounts that you want to use in the tax templates, go to:\n\n`> Home > Accounting > Chart of Accounts`\n\nSelect an account and click on edit. Select the 'Account Type' as 'Tax' for the account.\nIn this step we will guide you towards making the sales and taxes template.", + "description": "# Setting up Taxes\n\nAny sophisticated accounting system, including ERPNext will have automatic tax calculations for your transactions. These calculations are based on user defined rules in compliance to local rules and regulations.\n\nERPNext allows this via *Tax Templates*. These templates can be used in Sales Orders and Sales Invoices. Other types of charges that may apply to your invoices (like shipping, insurance etc.) can also be configured as taxes.\n\nFor Tax Accounts that you want to use in the tax templates, go to:\n\n`> Accounting > Taxes > Sales Taxes and Charges Template`\n\nYou can read more about these templates in our documentation [here](https://docs.erpnext.com/docs/user/manual/en/selling/sales-taxes-and-charges-template)\n", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-10-16 12:59:16.991287", + "modified": "2020-10-30 14:54:18.087383", "modified_by": "Administrator", "name": "Setup Taxes", "owner": "Administrator", "reference_document": "Sales Taxes and Charges Template", + "show_form_tour": 1, "show_full_form": 1, "title": "Lets create a Tax Template for Sales ", "validate_action": 0 From d6596a169cd2de4a4dfc74cf68d454cec290a30d Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 2 Nov 2020 15:07:48 +0530 Subject: [PATCH 053/728] 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 054/728] 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 f21e3fbf04c157ff74d40b88cb7bd4bc7d7578ca Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 3 Nov 2020 12:01:56 +0530 Subject: [PATCH 055/728] 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 056/728] 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 057/728] 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 e927224fbc45b54929825b015ad7da161bc984d9 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 4 Nov 2020 19:57:47 +0530 Subject: [PATCH 058/728] feat: update membership setting doctype * rename enable_auto_invoicing to enable_invoicing * add option to make_payment entry --- .../membership_settings.json | 42 +++++++++++++------ .../membership_type/membership_type.js | 4 +- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.json b/erpnext/non_profit/doctype/membership_settings/membership_settings.json index 5b6bab5b0a..a70c3c4b8a 100644 --- a/erpnext/non_profit/doctype/membership_settings/membership_settings.json +++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.json @@ -11,9 +11,11 @@ "billing_frequency", "webhook_secret", "column_break_6", - "enable_auto_invoicing", + "enable_invoicing", + "make_payment_entry", "company", "debit_account", + "payment_account", "column_break_9", "send_email", "send_invoice", @@ -58,14 +60,7 @@ "label": "Invoicing" }, { - "default": "0", - "fieldname": "enable_auto_invoicing", - "fieldtype": "Check", - "label": "Enable Auto Invoicing", - "mandatory_depends_on": "eval:doc.send_invoice" - }, - { - "depends_on": "eval:doc.enable_auto_invoicing", + "depends_on": "eval:doc.enable_invoicing", "fieldname": "debit_account", "fieldtype": "Link", "label": "Debit Account", @@ -77,7 +72,7 @@ "fieldtype": "Column Break" }, { - "depends_on": "eval:doc.enable_auto_invoicing", + "depends_on": "eval:doc.enable_invoicing", "fieldname": "company", "fieldtype": "Link", "label": "Company", @@ -86,7 +81,7 @@ }, { "default": "0", - "depends_on": "eval:doc.enable_auto_invoicing && doc.send_email", + "depends_on": "eval:doc.enable_invoicing && doc.send_email", "fieldname": "send_invoice", "fieldtype": "Check", "label": "Send Invoice with Email" @@ -119,11 +114,34 @@ "label": "Email Template", "mandatory_depends_on": "eval:doc.send_email", "options": "Email Template" + }, + { + "default": "0", + "fieldname": "enable_invoicing", + "fieldtype": "Check", + "label": "Enable Invoicing", + "mandatory_depends_on": "eval:doc.send_invoice || doc.make_payment_entry" + }, + { + "default": "0", + "depends_on": "eval:doc.enable_invoicing", + "fieldname": "make_payment_entry", + "fieldtype": "Check", + "label": "Make Payment Entry" + }, + { + "depends_on": "eval:doc.make_payment_entry", + "fieldname": "payment_account", + "fieldtype": "Link", + "label": "Payment To", + "mandatory_depends_on": "eval:doc.make_payment_entry", + "options": "Account" } ], + "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2020-08-05 17:26:37.287395", + "modified": "2020-11-04 19:51:21.990595", "modified_by": "Administrator", "module": "Non Profit", "name": "Membership Settings", diff --git a/erpnext/non_profit/doctype/membership_type/membership_type.js b/erpnext/non_profit/doctype/membership_type/membership_type.js index 43311a2c96..94ccdd8334 100644 --- a/erpnext/non_profit/doctype/membership_type/membership_type.js +++ b/erpnext/non_profit/doctype/membership_type/membership_type.js @@ -2,12 +2,12 @@ // For license information, please see license.txt frappe.ui.form.on('Membership Type', { - refresh: function(frm) { + refresh: function (frm) { frappe.db.get_single_value("Membership Settings", "enable_razorpay").then(val => { if (val) frm.set_df_property('razorpay_plan_id', 'hidden', false); }); - frappe.db.get_single_value("Membership Settings", "enable_auto_invoicing").then(val => { + frappe.db.get_single_value("Membership Settings", "enable_invoicing").then(val => { if (val) frm.set_df_property('linked_item', 'hidden', false); }); } From d75ff1a93e562ac5e22dc5afd2aef20fc8c62ab1 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 4 Nov 2020 20:17:33 +0530 Subject: [PATCH 059/728] feat: generate invoice on payment authorized --- erpnext/non_profit/doctype/membership/membership.py | 11 ++++++++--- .../membership_settings/membership_settings.json | 10 +++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index 4c85cb60e8..97de63b052 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -54,9 +54,14 @@ class Membership(Document): self.to_date = add_months(self.from_date, 1) def on_payment_authorized(self, status_changed_to=None): - if status_changed_to in ("Completed", "Authorized"): - self.load_from_db() - self.db_set('paid', 1) + if status_changed_to not in ("Completed", "Authorized"): + return + self.load_from_db() + self.db_set('paid', 1) + settings = frappe.get_doc("Membership Settings") + if settings.enable_invoicing and settings.create_for_web_forms: + self.generate_invoice(with_payment_entry=settings.make_payment_entry, save=True) + def generate_invoice(self, save=True): if not (self.paid or self.currency or self.amount): diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.json b/erpnext/non_profit/doctype/membership_settings/membership_settings.json index a70c3c4b8a..a25f5ffbc2 100644 --- a/erpnext/non_profit/doctype/membership_settings/membership_settings.json +++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.json @@ -12,6 +12,7 @@ "webhook_secret", "column_break_6", "enable_invoicing", + "create_for_web_forms", "make_payment_entry", "company", "debit_account", @@ -136,12 +137,19 @@ "label": "Payment To", "mandatory_depends_on": "eval:doc.make_payment_entry", "options": "Account" + }, + { + "depends_on": "eval:doc.enable_invoicing", + "description": "Automatically create an invoice when payment is authorized from a web form entry", + "fieldname": "create_for_web_forms", + "fieldtype": "Data", + "label": "Auto Create Invoice for Web Forms" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2020-11-04 19:51:21.990595", + "modified": "2020-11-04 20:19:55.163749", "modified_by": "Administrator", "module": "Non Profit", "name": "Membership Settings", From 9d9fa74e6b8dd5db1f89bc6bf92809c0fba29eda Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 9 Nov 2020 12:29:03 +0530 Subject: [PATCH 060/728] refactor(member): drop email column * remove email column * update controller methods * add patch to add value from email to email_id --- erpnext/non_profit/doctype/member/member.json | 10 +--------- .../doctype/membership/membership.py | 8 ++++++-- erpnext/patches.txt | 3 ++- .../v13_0/update_member_email_address.py | 19 +++++++++++++++++++ 4 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 erpnext/patches/v13_0/update_member_email_address.py diff --git a/erpnext/non_profit/doctype/member/member.json b/erpnext/non_profit/doctype/member/member.json index 992ef16d64..f190cfae75 100644 --- a/erpnext/non_profit/doctype/member/member.json +++ b/erpnext/non_profit/doctype/member/member.json @@ -12,7 +12,6 @@ "membership_expiry_date", "column_break_5", "membership_type", - "email", "email_id", "image", "customer_section", @@ -64,13 +63,6 @@ "options": "Membership Type", "reqd": 1 }, - { - "fieldname": "email", - "fieldtype": "Link", - "in_list_view": 1, - "label": "User", - "options": "User" - }, { "fieldname": "image", "fieldtype": "Attach Image", @@ -178,7 +170,7 @@ ], "image_field": "image", "links": [], - "modified": "2020-09-16 23:44:13.596948", + "modified": "2020-11-09 12:12:10.174647", "modified_by": "Administrator", "module": "Non Profit", "name": "Member", diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index 97de63b052..36f68bc00c 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -24,7 +24,7 @@ class Membership(Document): user = frappe.get_doc('User', frappe.session.user) member = frappe.get_doc(dict( doctype='Member', - email=frappe.session.user, + email_id=frappe.session.user, membership_type=self.membership_type, member_name=user.get_fullname() )).insert(ignore_permissions=True) @@ -97,8 +97,12 @@ class Membership(Document): frappe.throw(_("You need to enable Send Acknowledge Email in Membership Settings")) member = frappe.get_doc("Member", self.member) + + if not member.email_id: + frappe.throw(_("Email address of member {0} is missing").format(frappe.utils.get_link_to_form("Member", self.member))) + plan = frappe.get_doc("Membership Type", self.membership_type) - email = member.email_id if member.email_id else member.email + email = member.email_id attachments = [frappe.attach_print("Membership", self.name, print_format=settings.membership_print_format)] if self.invoice and settings.send_invoice: diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 34dbdd0bd5..a9cd25fd42 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -733,4 +733,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 -execute:frappe.delete_doc("Report", "Quoted Item Comparison") \ No newline at end of file +execute:frappe.delete_doc("Report", "Quoted Item Comparison") +erpnext.patches.v13_0.update_member_email_address \ No newline at end of file diff --git a/erpnext/patches/v13_0/update_member_email_address.py b/erpnext/patches/v13_0/update_member_email_address.py new file mode 100644 index 0000000000..da7828adbc --- /dev/null +++ b/erpnext/patches/v13_0/update_member_email_address.py @@ -0,0 +1,19 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + """add value to email_id column from email""" + + if frappe.db.has_column("Member", "email"): + # Get all members + for member in frappe.db.get_all("Member", pluck="name"): + # Check if email_id already exists + if not frappe.db.get_value("Member", member, "email_id"): + # fetch email id from the user linked field email + email = frappe.db.get_value("Member", member, "email") + + # Set the value for it + frappe.db.set_value("Member", member, "email_id", email) From e0f4dd0643a9ef59d81d70d35050f7e51cfcdc1d Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 9 Nov 2020 12:29:27 +0530 Subject: [PATCH 061/728] fix: fieldtype for auto_create_for_web_forms --- .../doctype/membership_settings/membership_settings.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.json b/erpnext/non_profit/doctype/membership_settings/membership_settings.json index a25f5ffbc2..961a9b9b3b 100644 --- a/erpnext/non_profit/doctype/membership_settings/membership_settings.json +++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.json @@ -139,17 +139,18 @@ "options": "Account" }, { + "default": "0", "depends_on": "eval:doc.enable_invoicing", "description": "Automatically create an invoice when payment is authorized from a web form entry", "fieldname": "create_for_web_forms", - "fieldtype": "Data", + "fieldtype": "Check", "label": "Auto Create Invoice for Web Forms" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2020-11-04 20:19:55.163749", + "modified": "2020-11-09 12:28:49.972434", "modified_by": "Administrator", "module": "Non Profit", "name": "Membership Settings", From 286ec04197e6cad7aac9a95d9c8996bc44006252 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 9 Nov 2020 12:53:00 +0530 Subject: [PATCH 062/728] test(membership): setup test defaults --- .../doctype/membership/membership.py | 2 + .../doctype/membership/test_membership.py | 47 ++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index 36f68bc00c..ae4df4a374 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -162,6 +162,8 @@ def get_member_based_on_subscription(subscription_id, email): return None def verify_signature(data): + if frappe.flags.in_test: + return True signature = frappe.request.headers.get('X-Razorpay-Signature') settings = frappe.get_doc("Membership Settings") diff --git a/erpnext/non_profit/doctype/membership/test_membership.py b/erpnext/non_profit/doctype/membership/test_membership.py index b23f4062a9..b62f19bd0d 100644 --- a/erpnext/non_profit/doctype/membership/test_membership.py +++ b/erpnext/non_profit/doctype/membership/test_membership.py @@ -2,8 +2,51 @@ # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt from __future__ import unicode_literals - import unittest +from erpnext.non_profit.doctype.member.member import create_member +from erpnext.stock.doctype.item.test_item import create_item class TestMembership(unittest.TestCase): - pass + def setUp(self): + # Get default company + company = frappe.get_doc("Company", erpnext.get_default_company()) + + # update membership settings + settings = frappe.get_doc("Membership Settings") + # Enable razorpay + settings.enable_razorpay = 1 + settings.billing_cycle = "Monthly" + settings.billing_frequency = 24 + # Enable invoicing + settings.enable_invoicing = 1 + settings.make_payment_entry = 1 + settings.company = company.name + settings.payment_to = company.default_cash_account + settings.debit_account = company.default_receivable_account + settings.save() + + # make test plan + plan = frappe.new_doc("Membership Type") + plan.amount = 100 + plan.razorpay_plan_id = "_rzpy_test_milythm" + plan.linked_item = create_item("_Test Item for Non Profit Membership") + plan.insert() + + # make test member + self.member_doc = create_member(frappe._dict({ + 'fullname': "_Test_Member", + 'email': "_test_member_erpnext@example.com", + 'plan_id': plan.name + })) + + def test_auto_generate_invoice_and_payment_entry(self): + pass + + def test_renew within_30_days(self): + pass + + def test_from_to_dates(self): + pass + + def test_razorpay_webook(self): + pass From c04321e64586ec7f466bb848530712320f4bfbe8 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 9 Nov 2020 13:29:46 +0530 Subject: [PATCH 063/728] test(membership): add test for invoicing and validation --- .../doctype/membership/membership.py | 16 +++- .../doctype/membership/test_membership.py | 78 ++++++++++++++++--- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index ae4df4a374..ac3b89a8d0 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -86,6 +86,20 @@ class Membership(Document): invoice = make_invoice(self, member, plan, settings) self.invoice = invoice.name + if with_payment_entry: + if not settings.payment_account: + frappe.throw(_("You need to set Payment Account in Membership Settings")) + + from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry + frappe.flags.ignore_account_permission=True + pe = get_payment_entry(dt='Sales Invoice', dn=invoice.name, bank_amount=invoice.grand_total) + frappe.flags.ignore_account_permission=False + pe.paid_to = settings.payment_account + pe.reference_no = self.name + pe.reference_date = getdate() + pe.save(ignore_permissions=True) + pe.submit() + if save: self.save() @@ -97,7 +111,7 @@ class Membership(Document): frappe.throw(_("You need to enable Send Acknowledge Email in Membership Settings")) member = frappe.get_doc("Member", self.member) - + if not member.email_id: frappe.throw(_("Email address of member {0} is missing").format(frappe.utils.get_link_to_form("Member", self.member))) diff --git a/erpnext/non_profit/doctype/membership/test_membership.py b/erpnext/non_profit/doctype/membership/test_membership.py index b62f19bd0d..6e4885d013 100644 --- a/erpnext/non_profit/doctype/membership/test_membership.py +++ b/erpnext/non_profit/doctype/membership/test_membership.py @@ -3,7 +3,10 @@ # See license.txt from __future__ import unicode_literals import unittest +import frappe +import erpnext from erpnext.non_profit.doctype.member.member import create_member +from frappe.utils import nowdate, getdate, add_months from erpnext.stock.doctype.item.test_item import create_item class TestMembership(unittest.TestCase): @@ -21,15 +24,16 @@ class TestMembership(unittest.TestCase): settings.enable_invoicing = 1 settings.make_payment_entry = 1 settings.company = company.name - settings.payment_to = company.default_cash_account + settings.payment_account = company.default_cash_account settings.debit_account = company.default_receivable_account settings.save() # make test plan plan = frappe.new_doc("Membership Type") + plan.membership_type = "_rzpy_test_milythm" plan.amount = 100 plan.razorpay_plan_id = "_rzpy_test_milythm" - plan.linked_item = create_item("_Test Item for Non Profit Membership") + plan.linked_item = create_item("_Test Item for Non Profit Membership").name plan.insert() # make test member @@ -38,15 +42,71 @@ class TestMembership(unittest.TestCase): 'email': "_test_member_erpnext@example.com", 'plan_id': plan.name })) + self.member_doc.make_customer_and_link() + self.member = "self.member_doc.name" def test_auto_generate_invoice_and_payment_entry(self): - pass + entry = make_membership(self.member) - def test_renew within_30_days(self): - pass + # Naive test to see if at all invoice was generated and attached to member + # In any case if details were missing, the invoicing would throw an error + invoice = entry.generate_invoice(save=True) + self.assertEqual(invoice.name, entry.invoice) + # entry.delete() - def test_from_to_dates(self): - pass + # # Remove customer + # old_customer = self.member_doc.customer + # self.member_doc.customer = None + # self.member_doc.save() - def test_razorpay_webook(self): - pass + # entry = make_membership(self.member) + # self.assertRaises(frappe.ValidationError, entry.generate_invoice) + + # # Add customer value back + # self.member_doc.customer = old_customer + # self.member_doc.save() + + # # Remove company + # set_config(company, None) + # self.assertRaises(frappe.ValidationError, entry.generate_invoice) + + def test_renew_within_30_days(self): + # create a membership for two months + # Should work fine + make_membership(self.member, { "from_date": nowdate() }) + make_membership(self.member, { "from_date": add_months(nowdate(), 1) }) + + from frappe.utils.user import add_role + add_role("test@example.com", "Non Profit Manager") + frappe.set_user("test@example.com") + + # create next membership with expiry not within 30 days + self.assertRaises(frappe.ValidationError, make_membership, self.member, { + "from_date": add_months(nowdate(), 2), + }) + + frappe.set_user("Administrator") + # create the same membership but as administrator + new_entry = make_membership(self.member, { + "from_date": add_months(nowdate(), 2), + "to_date": add_months(nowdate(), 3), + }) + +def set_config(key, value): + frappe.db.set_value("Membership Settings", None, key, value) + +def make_membership(member, payload={}): + data = { + "doctype": "Membership", + "member": member, + "membership_status": "Current", + "membership_type": "_rzpy_test_milythm", + "currency": "INR", + "paid": 1, + "from_date": nowdate(), + "amount": 100 + } + data.update(payload) + membership = frappe.get_doc(data) + membership.insert(ignore_permissions=True, ignore_if_duplicate=True) + return membership \ No newline at end of file From 7e1cdf9b978ffdb6713a2e2cade4ac7307b73533 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 9 Nov 2020 14:01:11 +0530 Subject: [PATCH 064/728] feat(breaking): update get_last_membership to fetch correct details --- erpnext/__init__.py | 14 ++++---------- .../non_profit/doctype/membership/membership.py | 2 +- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 38d8a62f07..5a5c448026 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -132,16 +132,10 @@ def allow_regional(fn): return caller -def get_last_membership(): +def get_last_membership(member): '''Returns last membership if exists''' last_membership = frappe.get_all('Membership', 'name,to_date,membership_type', - dict(member=frappe.session.user, paid=1), order_by='to_date desc', limit=1) + dict(member=member, paid=1), order_by='to_date desc', limit=1) - return last_membership and last_membership[0] - -def is_member(): - '''Returns true if the user is still a member''' - last_membership = get_last_membership() - if last_membership and getdate(last_membership.to_date) > getdate(): - return True - return False + if last_membership: + return last_membership[0] diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index ac3b89a8d0..7c83a4e0da 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -34,7 +34,7 @@ class Membership(Document): self.member = member_name # get last membership (if active) - last_membership = erpnext.get_last_membership() + last_membership = erpnext.get_last_membership(self.member) # if person applied for offline membership if last_membership and not frappe.session.user == "Administrator": From 12fafa3e7a20bb8a2ad54ea43f8e3d2146bd30b5 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 9 Nov 2020 14:01:22 +0530 Subject: [PATCH 065/728] chore: remove validation for old member field --- erpnext/non_profit/doctype/member/member.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py index 44b975e9e9..7fc4f225aa 100644 --- a/erpnext/non_profit/doctype/member/member.py +++ b/erpnext/non_profit/doctype/member/member.py @@ -18,8 +18,6 @@ class Member(Document): def validate(self): - if self.email: - self.validate_email_type(self.email) if self.email_id: self.validate_email_type(self.email_id) From 723e220a3409150de11c4b74a7bbb5911060382b Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 9 Nov 2020 14:01:52 +0530 Subject: [PATCH 066/728] chore: remove commented code --- .../doctype/membership/test_membership.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/erpnext/non_profit/doctype/membership/test_membership.py b/erpnext/non_profit/doctype/membership/test_membership.py index 6e4885d013..ce31b91956 100644 --- a/erpnext/non_profit/doctype/membership/test_membership.py +++ b/erpnext/non_profit/doctype/membership/test_membership.py @@ -52,23 +52,6 @@ class TestMembership(unittest.TestCase): # In any case if details were missing, the invoicing would throw an error invoice = entry.generate_invoice(save=True) self.assertEqual(invoice.name, entry.invoice) - # entry.delete() - - # # Remove customer - # old_customer = self.member_doc.customer - # self.member_doc.customer = None - # self.member_doc.save() - - # entry = make_membership(self.member) - # self.assertRaises(frappe.ValidationError, entry.generate_invoice) - - # # Add customer value back - # self.member_doc.customer = old_customer - # self.member_doc.save() - - # # Remove company - # set_config(company, None) - # self.assertRaises(frappe.ValidationError, entry.generate_invoice) def test_renew_within_30_days(self): # create a membership for two months From 4f2a64479dfcaf6a4bc164315abb4865723f75fe Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 9 Nov 2020 17:00:09 +0530 Subject: [PATCH 067/728] 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 068/728] 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 e3b71b9b249946b2e8c8d0d5ebe5096ac7f038bf Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 10 Nov 2020 17:23:24 +0530 Subject: [PATCH 069/728] fix: compact item print style --- .../print_formats/includes/item_table_description.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/templates/print_formats/includes/item_table_description.html b/erpnext/templates/print_formats/includes/item_table_description.html index ed46c9e224..7569e50b45 100644 --- a/erpnext/templates/print_formats/includes/item_table_description.html +++ b/erpnext/templates/print_formats/includes/item_table_description.html @@ -11,11 +11,11 @@
{% if doc.in_format_data("item_code") and "item_code" in display_columns -%} -
- {% if compact %}{% endif %} - {{ _(doc.item_code) }} - {% if compact %}{% endif %} + {% if compact %} +
+ {{ _(doc.item_code) }}
+ {% endif %} {%- endif %} {%- if doc.in_format_data("item_name") and "item_name" in display_columns and From 1c969d64a2f928f78470c4dd42899d2393c6291f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Nov 2020 20:25:35 +0530 Subject: [PATCH 070/728] 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 071/728] 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 13d1dda74b88c8c46d0e3adf618433e687c6cda2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 11 Nov 2020 11:07:17 +0530 Subject: [PATCH 072/728] 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 073/728] 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 074/728] 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 c1b0e65f9f9df5fdad4a813a788f4f458b2e8318 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 12 Nov 2020 09:54:44 +0530 Subject: [PATCH 075/728] feat: Putaway --- .../stock/doctype/putaway_rule/__init__.py | 0 .../doctype/putaway_rule/putaway_rule.js | 18 +++ .../doctype/putaway_rule/putaway_rule.json | 111 ++++++++++++++++++ .../doctype/putaway_rule/putaway_rule.py | 93 +++++++++++++++ .../doctype/putaway_rule/putaway_rule_list.js | 10 ++ .../doctype/putaway_rule/test_putaway_rule.py | 10 ++ 6 files changed, 242 insertions(+) create mode 100644 erpnext/stock/doctype/putaway_rule/__init__.py create mode 100644 erpnext/stock/doctype/putaway_rule/putaway_rule.js create mode 100644 erpnext/stock/doctype/putaway_rule/putaway_rule.json create mode 100644 erpnext/stock/doctype/putaway_rule/putaway_rule.py create mode 100644 erpnext/stock/doctype/putaway_rule/putaway_rule_list.js create mode 100644 erpnext/stock/doctype/putaway_rule/test_putaway_rule.py diff --git a/erpnext/stock/doctype/putaway_rule/__init__.py b/erpnext/stock/doctype/putaway_rule/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.js b/erpnext/stock/doctype/putaway_rule/putaway_rule.js new file mode 100644 index 0000000000..ae08e82c28 --- /dev/null +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.js @@ -0,0 +1,18 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Putaway Rule', { + setup: function(frm) { + frm.set_query("warehouse", function() { + return { + "filters": { + "company": frm.doc.company, + "is_group": 0 + } + }; + }); + } + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.json b/erpnext/stock/doctype/putaway_rule/putaway_rule.json new file mode 100644 index 0000000000..6a132c7e25 --- /dev/null +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.json @@ -0,0 +1,111 @@ +{ + "actions": [], + "autoname": "format:{item_code}-{warehouse}", + "creation": "2020-11-09 11:39:46.489501", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "disable", + "item_code", + "item_name", + "warehouse", + "col_break_capacity", + "company", + "capacity", + "priority", + "stock_uom" + ], + "fields": [ + { + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Item", + "options": "Item", + "reqd": 1 + }, + { + "fetch_from": "item_code.item_name", + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name", + "read_only": 1 + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Warehouse", + "options": "Warehouse", + "reqd": 1 + }, + { + "fieldname": "col_break_capacity", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "capacity", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Capacity", + "reqd": 1 + }, + { + "default": "item_code.stock_uom", + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM", + "read_only": 1 + }, + { + "default": "1", + "fieldname": "priority", + "fieldtype": "Int", + "label": "Priority" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "default": "0", + "depends_on": "eval:!doc.__islocal", + "fieldname": "disable", + "fieldtype": "Check", + "label": "Disable" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2020-11-10 17:06:27.151335", + "modified_by": "Administrator", + "module": "Stock", + "name": "Putaway Rule", + "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/putaway_rule/putaway_rule.py b/erpnext/stock/doctype/putaway_rule/putaway_rule.py new file mode 100644 index 0000000000..9f02833431 --- /dev/null +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py @@ -0,0 +1,93 @@ +# -*- 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 copy +from frappe import _ +from frappe.utils import flt +from frappe.model.document import Document + +class PutawayRule(Document): + def validate(self): + self.validate_duplicate_rule() + self.validate_warehouse_and_company() + self.validate_capacity() + self.validate_priority() + + def validate_duplicate_rule(self): + existing_rule = frappe.db.exists("Putaway Rule", {"item_code": self.item_code, "warehouse": self.warehouse}) + if existing_rule and existing_rule != self.name: + frappe.throw(_("Putaway Rule already exists for Item {0} in Warehouse {1}.") + .format(frappe.bold(self.item_code), frappe.bold(self.warehouse)), + title=_("Duplicate")) + + def validate_priority(self): + if self.priority < 1: + frappe.throw(_("Priority cannot be lesser than 1."), title=_("Invalid Priority")) + + def validate_warehouse_and_company(self): + company = frappe.db.get_value("Warehouse", self.warehouse, "company") + if company != self.company: + frappe.throw(_("Warehouse {0} does not belong to Company {1}.") + .format(frappe.bold(self.warehouse), frappe.bold(self.company)), + title=_("Invalid Warehouse")) + + def validate_capacity(self): + # check if capacity is lesser than current balance in warehouse + pass + +@frappe.whitelist() +def get_ordered_putaway_rules(item_code, company, qty): + """Returns an ordered list of putaway rules to apply on an item.""" + + # get enabled putaway rules for this item code in this company that have pending capacity + # order the rules by priority first + # if same priority, order by pending capacity (capacity - get how much stock is in the warehouse) + # return this list + # [{'name': "something", "free space": 20}, {'name': "something", "free space": 10}] + +@frappe.whitelist() +def apply_putaway_rule(items, company): + """ Applies Putaway Rule on line items. + + items: List of line items in a Purchase Receipt + company: Company in Purchase Receipt + """ + items_not_accomodated = [] + for item in items: + item_qty = item.qty + at_capacity, rules = get_ordered_putaway_rules(item.item_code, company, item_qty) + + if not rules: + if at_capacity: + items_not_accomodated.append([item.item_code, item_qty]) + continue + + item_row_updated = False + for rule in rules: + while item_qty > 0: + if not item_row_updated: + # update pre-existing row + item.qty = rule.qty + item.warehouse = rule.warehouse + item_row_updated = True + else: + # add rows for split quantity + added_row = copy.deepcopy(item) + added_row.qty = rule.qty + added_row.warehouse = rule.warehouse + items.append(added_row) + + item_qty -= flt(rule.qty) + + # if pending qty after applying rules, add row without warehouse + if item_qty > 0: + added_row = copy.deepcopy(item) + added_row.qty = item_qty + added_row.warehouse = '' + items.append(added_row) + items_not_accomodated.append([item.item_code, item_qty]) + + # need to check pricing rule, item tax impact \ No newline at end of file diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js b/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js new file mode 100644 index 0000000000..bb1654cf24 --- /dev/null +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js @@ -0,0 +1,10 @@ +frappe.listview_settings['Putaway Rule'] = { + add_fields: ["disable"], + get_indicator: (doc) => { + if (doc.disable) { + return [__("Disabled"), "darkgrey", "disable,=,1"]; + } else { + return [__("Active"), "blue", "disable,=,0"]; + }; + } +}; diff --git a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py new file mode 100644 index 0000000000..e262217f84 --- /dev/null +++ b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.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 TestPutawayRule(unittest.TestCase): + pass From b58dca8d942c18bc3ab0e1afa7ca7e744967c5c8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 12 Nov 2020 13:37:11 +0530 Subject: [PATCH 076/728] 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 077/728] 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 078/728] 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 079/728] 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 8b6370bb45232d0e39508a1329258388c4d47439 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 15 Nov 2020 22:41:36 +0530 Subject: [PATCH 080/728] feat: Add accounting dimension filter doctype --- .../accounting_dimension_filter/__init__.py | 0 .../accounting_dimension_filter.js | 32 ++++++ .../accounting_dimension_filter.json | 100 ++++++++++++++++++ .../accounting_dimension_filter.py | 63 +++++++++++ .../test_accounting_dimension_filter.py | 10 ++ .../doctype/allowed_dimension/__init__.py | 0 .../allowed_dimension/allowed_dimension.json | 43 ++++++++ .../allowed_dimension/allowed_dimension.py | 10 ++ .../doctype/applicable_on_account/__init__.py | 0 .../applicable_on_account.json | 35 ++++++ .../applicable_on_account.py | 10 ++ 11 files changed, 303 insertions(+) create mode 100644 erpnext/accounts/doctype/accounting_dimension_filter/__init__.py create mode 100644 erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js create mode 100644 erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json create mode 100644 erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py create mode 100644 erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py create mode 100644 erpnext/accounts/doctype/allowed_dimension/__init__.py create mode 100644 erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json create mode 100644 erpnext/accounts/doctype/allowed_dimension/allowed_dimension.py create mode 100644 erpnext/accounts/doctype/applicable_on_account/__init__.py create mode 100644 erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json create mode 100644 erpnext/accounts/doctype/applicable_on_account/applicable_on_account.py diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/__init__.py b/erpnext/accounts/doctype/accounting_dimension_filter/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js new file mode 100644 index 0000000000..6c254fcfb7 --- /dev/null +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js @@ -0,0 +1,32 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Accounting Dimension Filter', { + onload: function(frm) { + frappe.db.get_list('Accounting Dimension', + {fields: ['name']}).then((res) => { + let options = ['Cost Center', 'Project']; + + res.forEach((dimension) => { + options.push(dimension.name); + }); + + frm.set_df_property('accounting_dimension', 'options', options); + }); + }, + + accounting_dimension: function(frm) { + frm.clear_table("dimensions"); + let row = frm.add_child("dimensions"); + row.accounting_dimension = frm.doc.accounting_dimension; + frm.refresh_field("dimensions"); + }, +}); + +frappe.ui.form.on('Allowed Dimension', { + dimensions_add: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + row.accounting_dimension = frm.doc.accounting_dimension; + frm.refresh_field("dimensions"); + } +}); \ No newline at end of file diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json new file mode 100644 index 0000000000..e626a09ce0 --- /dev/null +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json @@ -0,0 +1,100 @@ +{ + "actions": [], + "autoname": "format:{accounting_dimension}-{#####}", + "creation": "2020-11-08 18:28:11.906146", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "accounting_dimension", + "column_break_2", + "allow_or_restrict", + "section_break_4", + "accounts", + "column_break_6", + "dimensions" + ], + "fields": [ + { + "fieldname": "accounting_dimension", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Accounting Dimension", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "hide_border": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "allow_or_restrict", + "fieldtype": "Select", + "label": "Allow Or Restrict Dimension", + "options": "Allow\nRestrict", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Accounts", + "options": "Applicable On Account", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "depends_on": "eval:doc.accounting_dimension", + "fieldname": "dimensions", + "fieldtype": "Table", + "label": "Dimensions", + "options": "Allowed Dimension", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2020-11-14 18:02:02.616932", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Accounting Dimension Filter", + "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/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py new file mode 100644 index 0000000000..ccfafd96ed --- /dev/null +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.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 import _, scrub +from frappe.model.document import Document + +class AccountingDimensionFilter(Document): + def validate(self): + self.validate_applicable_accounts() + + def validate_applicable_accounts(self): + accounts = frappe.db.sql( + """ + SELECT a.applicable_on_account as account + FROM `tabApplicable On Account` a, `tabAccounting Dimension Filter` d + WHERE d.name = a.parent + and d.name != %s + and d.accounting_dimension = %s + """, (self.name, self.accounting_dimension), as_dict=1) + + account_list = [d.account for d in accounts] + + for account in self.get('accounts'): + if account.applicable_on_account in account_list: + frappe.throw(_("Row {0}: {1} account already applied for Accounting Dimension {2}").format( + account.idx, frappe.bold(account.applicable_on_account), frappe.bold(self.accounting_dimension))) + +def get_dimension_filter_map(): + filters = frappe.db.sql( + """ SELECT + a.applicable_on_account, d.dimension_value, p.accounting_dimension, + p.allow_or_restrict, ad.fieldname + FROM + `tabApplicable On Account` a, `tabAllowed Dimension` d, + `tabAccounting Dimension Filter` p, `tabAccounting Dimension` ad + WHERE + p.name = a.parent + AND p.name = d.parent + AND (p.accounting_dimension = ad.name + OR p.accounting_dimension in ('Cost Center', 'Project')) + """, as_dict=1) + + dimension_filter_map = {} + account_filter_map = {} + + for f in filters: + if f.accounting_dimension in ('Cost Center', 'Project'): + f.fieldname = scrub(f.accounting_dimension) + + build_map(dimension_filter_map, f.fieldname, f.applicable_on_account, f.dimension_value, + f.allow_or_restrict) + + return dimension_filter_map + +def build_map(map_object, dimension, account, filter_value, allow_or_restrict): + map_object.setdefault((dimension, account), { + 'allowed_dimensions': [], + 'allow_or_restrict': allow_or_restrict + }) + map_object[(dimension, account)]['allowed_dimensions'].append(filter_value) diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py new file mode 100644 index 0000000000..c271a25fbd --- /dev/null +++ b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.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 TestAccountingDimensionFilter(unittest.TestCase): + pass diff --git a/erpnext/accounts/doctype/allowed_dimension/__init__.py b/erpnext/accounts/doctype/allowed_dimension/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json new file mode 100644 index 0000000000..20024b0322 --- /dev/null +++ b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json @@ -0,0 +1,43 @@ +{ + "actions": [], + "creation": "2020-11-08 18:22:36.001131", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "accounting_dimension", + "dimension_value" + ], + "fields": [ + { + "fieldname": "accounting_dimension", + "fieldtype": "Link", + "label": "Accounting Dimension", + "options": "DocType", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "dimension_value", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "options": "accounting_dimension", + "show_days": 1, + "show_seconds": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-11-14 19:54:03.269016", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Allowed Dimension", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.py b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.py new file mode 100644 index 0000000000..c2afc1a262 --- /dev/null +++ b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.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 AllowedDimension(Document): + pass diff --git a/erpnext/accounts/doctype/applicable_on_account/__init__.py b/erpnext/accounts/doctype/applicable_on_account/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json new file mode 100644 index 0000000000..8305da2ba0 --- /dev/null +++ b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json @@ -0,0 +1,35 @@ +{ + "actions": [], + "creation": "2020-11-08 18:20:00.944449", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "applicable_on_account" + ], + "fields": [ + { + "fieldname": "applicable_on_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Applicable On Account", + "options": "Account", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-11-14 16:54:06.756883", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Applicable On 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/accounts/doctype/applicable_on_account/applicable_on_account.py b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.py new file mode 100644 index 0000000000..0fccaf302f --- /dev/null +++ b/erpnext/accounts/doctype/applicable_on_account/applicable_on_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 ApplicableOnAccount(Document): + pass From 96e874bfda3e93a48613765c7433824587fb0360 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 15 Nov 2020 22:43:01 +0530 Subject: [PATCH 081/728] fix: dimension filter query --- .../accounting_dimension.py | 14 +++- erpnext/controllers/queries.py | 47 ++++++++++++++ .../public/js/utils/dimension_tree_filter.js | 65 ++++++++++++++----- 3 files changed, 109 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index f888d9e038..b9d4da289e 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -203,7 +203,7 @@ def get_dimension_with_children(doctype, dimension): return all_dimensions @frappe.whitelist() -def get_dimension_filters(): +def get_dimension_filters(with_costcenter_and_project=False): dimension_filters = frappe.db.sql(""" SELECT label, fieldname, document_type FROM `tabAccounting Dimension` @@ -214,6 +214,18 @@ def get_dimension_filters(): FROM `tabAccounting Dimension Detail` c, `tabAccounting Dimension` p WHERE c.parent = p.name""", as_dict=1) + if with_costcenter_and_project: + dimension_filters.extend([ + { + 'fieldname': 'cost_center', + 'document_type': 'Cost Center' + }, + { + 'fieldname': 'project', + 'document_type': 'Project' + } + ]) + default_dimensions_map = {} for dimension in default_dimensions: default_dimensions_map.setdefault(dimension.company, {}) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 8fe3816c24..015807d563 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -493,6 +493,53 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters): 'company': filters.get("company", "") }) +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters): + from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import get_dimension_filter_map + dimension_filters = get_dimension_filter_map() + dimension_filters = dimension_filters.get((filters.get('dimension'),filters.get('account'))) + group_condition = '' + company_condition = '' + + meta = frappe.get_meta(doctype) + + if meta.is_tree: + group_condition = 'and is_group = 0 ' + + if meta.has_field('company'): + company_condition = 'and company = %s ' % (frappe.db.escape(filters.get('company'))) + + if dimension_filters: + if dimension_filters['allow_or_restrict'] == 'Allow': + query_selector = 'in' + else: + query_selector = 'not in' + + if len(dimension_filters['allowed_dimensions']) == 1: + dimensions = tuple(dimension_filters['allowed_dimensions'] * 2) + else: + dimensions = tuple(dimension_filters['allowed_dimensions']) + + result = frappe.db.sql("""SELECT name from `tab{doctype}` where + name {query_selector} {restricted} + {group_condition} {company_condition} + and {key} LIKE %(txt)s""".format( + doctype=doctype, query_selector=query_selector, restricted=dimensions, + group_condition = group_condition, + company_condition = company_condition, + key=searchfield), { + 'txt': '%' + txt + '%' + }) + + return result + else: + return frappe.db.sql(""" + SELECT name from `tab{doctype}` where + {key} LIKE %(txt)s {group_condition} {company_condition}""" + .format(doctype=doctype, key=searchfield, + group_condition=group_condition, company_condition=company_condition), + { 'txt': '%' + txt + '%'}) @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index b6720c05cb..34b563553e 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -5,14 +5,18 @@ let default_dimensions = {}; let doctypes_with_dimensions = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset", "Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Shipping Rule", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation", "Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", - "Subscription", "Purchase Order", "Journal Entry", "Material Request", "Purchase Receipt", "Landed Cost Item", "Asset"]; + "Subscription", "Purchase Order", "Journal Entry", "Material Request", "Purchase Receipt", "Asset", "Asset Value Adjustment"]; let child_docs = ["Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item", "Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", - "Landed Cost Item", "Asset Value Adjustment", "Opening Invoice Creation Tool Item", "Subscription Plan"]; + "Landed Cost Item", "Asset Value Adjustment", "Opening Invoice Creation Tool Item", "Subscription Plan", + "Sales Taxes and Charges", "Purchase Taxes and Charges"]; frappe.call({ method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimension_filters", + args: { + 'with_costcenter_and_project': true + }, callback: function(r) { erpnext.dimension_filters = r.message[0]; default_dimensions = r.message[1]; @@ -24,11 +28,16 @@ doctypes_with_dimensions.forEach((doctype) => { onload: function(frm) { erpnext.dimension_filters.forEach((dimension) => { frappe.model.with_doctype(dimension['document_type'], () => { - if(frappe.meta.has_field(dimension['document_type'], 'is_group')) { - frm.set_query(dimension['fieldname'], { - "is_group": 0 - }); - } + let parent_fields = []; + frappe.meta.get_docfields(doctype).forEach((df) => { + if (df.fieldtype === 'Link' && df.options === 'Account') { + parent_fields.push(df.fieldname); + } else if (df.fieldtype === 'Table') { + setup_child_filters(frm, df.options, df.fieldname, dimension['fieldname']); + }; + + setup_account_filters(frm, dimension['fieldname'], parent_fields); + }); }); }); }, @@ -67,17 +76,41 @@ doctypes_with_dimensions.forEach((doctype) => { child_docs.forEach((doctype) => { frappe.ui.form.on(doctype, { items_add: function(frm, cdt, cdn) { - erpnext.dimension_filters.forEach((dimension) => { - var row = frappe.get_doc(cdt, cdn); - frm.script_manager.copy_from_first_row("items", row, [dimension['fieldname']]); - }); + copy_dimension(frm, cdt, cdn, "items"); }, accounts_add: function(frm, cdt, cdn) { - erpnext.dimension_filters.forEach((dimension) => { - var row = frappe.get_doc(cdt, cdn); - frm.script_manager.copy_from_first_row("accounts", row, [dimension['fieldname']]); - }); + copy_dimension(frm, cdt, cdn, "accounts"); } }); -}); \ No newline at end of file +}); + +let copy_dimension = function(frm, cdt, cdn, fieldname) { + erpnext.dimension_filters.forEach((dimension) => { + let row = frappe.get_doc(cdt, cdn); + frm.script_manager.copy_from_first_row(fieldname, row, [dimension['fieldname']]); + }); +} + +let setup_child_filters = function(frm, doctype, parentfield, dimension) { + let fields = []; + + frappe.model.with_doctype(doctype, () => { + frappe.meta.get_docfields(doctype).forEach((df) => { + if (df.fieldtype === 'Link' && df.options === 'Account') { + fields.push(df.fieldname); + } + }); + + frm.set_query(dimension, parentfield, function(doc, cdt, cdn) { + let row = locals[cdt][cdn]; + return erpnext.queries.get_filtered_dimensions(row, fields, dimension, doc.company); + }); + }); +} + +let setup_account_filters = function(frm, dimension, fields) { + frm.set_query(dimension, function(doc) { + return erpnext.queries.get_filtered_dimensions(doc, fields, dimension, doc.company); + }); +} \ No newline at end of file From 6e5748e2a3344eeead1a7b1f86258de233276d02 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 15 Nov 2020 22:43:48 +0530 Subject: [PATCH 082/728] fix: dimension filter query --- erpnext/public/js/queries.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js index 560a5617da..7b7a9df1ac 100644 --- a/erpnext/public/js/queries.js +++ b/erpnext/public/js/queries.js @@ -116,6 +116,25 @@ $.extend(erpnext.queries, { ] } + }, + + get_filtered_dimensions: function(doc, child_fields, dimension, company) { + let account = ''; + + child_fields.forEach((field) => { + if (!account) { + account = doc[field]; + } + }); + + return { + query: "erpnext.controllers.queries.get_filtered_dimensions", + filters: { + 'dimension': dimension, + 'account': account, + 'company': company + } + } } }); From f916bc048ff08a4bbae87e37bd8215c35ac32f8a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 15 Nov 2020 22:44:39 +0530 Subject: [PATCH 083/728] fix: Remove cost center query from doctypes --- erpnext/accounts/doctype/budget/budget.js | 12 ++---------- .../accounts/doctype/journal_entry/journal_entry.js | 9 --------- .../doctype/loyalty_program/loyalty_program.js | 8 -------- .../accounts/doctype/payment_entry/payment_entry.js | 9 --------- .../doctype/purchase_invoice/purchase_invoice.js | 9 --------- .../accounts/doctype/sales_invoice/sales_invoice.js | 9 --------- .../accounts/doctype/shipping_rule/shipping_rule.js | 8 -------- erpnext/assets/doctype/asset/asset.js | 8 -------- erpnext/hr/doctype/expense_claim/expense_claim.js | 9 --------- .../payroll/doctype/payroll_entry/payroll_entry.js | 9 +-------- erpnext/public/js/controllers/accounts.js | 9 --------- erpnext/public/js/controllers/transaction.js | 10 ---------- 12 files changed, 3 insertions(+), 106 deletions(-) diff --git a/erpnext/accounts/doctype/budget/budget.js b/erpnext/accounts/doctype/budget/budget.js index cadf1e7e0c..48cc493522 100644 --- a/erpnext/accounts/doctype/budget/budget.js +++ b/erpnext/accounts/doctype/budget/budget.js @@ -3,14 +3,6 @@ frappe.ui.form.on('Budget', { onload: function(frm) { - frm.set_query("cost_center", function() { - return { - filters: { - company: frm.doc.company - } - } - }) - frm.set_query("project", function() { return { filters: { @@ -18,7 +10,7 @@ frappe.ui.form.on('Budget', { } } }) - + frm.set_query("account", "accounts", function() { return { filters: { @@ -28,7 +20,7 @@ frappe.ui.form.on('Budget', { } } }) - + frm.set_query("monthly_distribution", function() { return { filters: { diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index ff12967155..d60a7b76cc 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -222,15 +222,6 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ return erpnext.journal_entry.account_query(me.frm); }); - me.frm.set_query("cost_center", "accounts", function(doc, cdt, cdn) { - return { - filters: { - company: me.frm.doc.company, - is_group: 0 - } - }; - }); - me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) { const row = locals[cdt][cdn]; diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.js b/erpnext/accounts/doctype/loyalty_program/loyalty_program.js index 524a671801..0d2b8cbf15 100644 --- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.js +++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.js @@ -46,14 +46,6 @@ frappe.ui.form.on('Loyalty Program', { }; }); - frm.set_query("cost_center", function() { - return { - filters: { - company: frm.doc.company - } - }; - }); - frm.set_value("company", frappe.defaults.get_user_default("Company")); }, diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index e117471738..ea5487d575 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -88,15 +88,6 @@ frappe.ui.form.on('Payment Entry', { } }); - frm.set_query("cost_center", "deductions", function() { - return { - filters: { - "is_group": 0, - "company": frm.doc.company - } - } - }); - frm.set_query("reference_doctype", "references", function() { if (frm.doc.party_type=="Customer") { var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"]; diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 1d41d0fa2a..3c07ee75cb 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -501,15 +501,6 @@ frappe.ui.form.on("Purchase Invoice", { } } } - - frm.set_query("cost_center", function() { - return { - filters: { - company: frm.doc.company, - is_group: 0 - } - }; - }); }, onload: function(frm) { diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 502e65ed8d..e27bd2fa3a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -571,15 +571,6 @@ frappe.ui.form.on('Sales Invoice', { }; }); - frm.set_query("cost_center", function() { - return { - filters: { - company: frm.doc.company, - is_group: 0 - } - }; - }); - frm.custom_make_buttons = { 'Delivery Note': 'Delivery', 'Sales Invoice': 'Sales Return', diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js index d0904eec3e..7cfbfed138 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js @@ -3,14 +3,6 @@ frappe.ui.form.on('Shipping Rule', { refresh: function(frm) { - frm.set_query("cost_center", function() { - return { - filters: { - company: frm.doc.company - } - } - }) - frm.set_query("account", function() { return { filters: { diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 7ad164a8b9..3af3948fac 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -31,14 +31,6 @@ frappe.ui.form.on('Asset', { } }; }); - - frm.set_query("cost_center", function() { - return { - "filters": { - "company": frm.doc.company, - } - }; - }); }, setup: function(frm) { diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 221300b519..cbafd7d3ac 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -167,15 +167,6 @@ frappe.ui.form.on("Expense Claim", { }; }); - frm.set_query("cost_center", "expenses", function() { - return { - filters: { - "company": frm.doc.company, - "is_group": 0 - } - }; - }); - frm.set_query("payable_account", function() { return { filters: { diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js index 1abc869c53..96006158b6 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js @@ -113,14 +113,7 @@ frappe.ui.form.on('Payroll Entry', { } }; }), - frm.set_query("cost_center", function () { - return { - filters: { - "is_group": 0, - company: frm.doc.company - } - }; - }), + frm.set_query("project", function () { return { filters: { diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 6e97d811fc..45c494e3e5 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -31,15 +31,6 @@ frappe.ui.form.on(cur_frm.doctype, { } } }); - - frm.set_query("cost_center", "taxes", function(doc) { - return { - filters: { - 'company': doc.company, - "is_group": 0 - } - } - }); } }, validate: function(frm) { diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 1358a4bd08..ec6b3dc6df 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -159,16 +159,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }; }); } - if (this.frm.fields_dict["items"].grid.get_field("cost_center")) { - this.frm.set_query("cost_center", "items", function(doc) { - return { - filters: { - "company": doc.company, - "is_group": 0 - } - }; - }); - } if (this.frm.fields_dict["items"].grid.get_field("expense_account")) { this.frm.set_query("expense_account", "items", function(doc) { From 1f9b03345dd6360e5b8fc2df98948cebe1e53a1e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Nov 2020 09:55:35 +0530 Subject: [PATCH 084/728] fix: Remove project filter --- erpnext/accounts/doctype/budget/budget.js | 8 -------- erpnext/payroll/doctype/payroll_entry/payroll_entry.js | 10 +--------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/budget/budget.js b/erpnext/accounts/doctype/budget/budget.js index 48cc493522..1b79398247 100644 --- a/erpnext/accounts/doctype/budget/budget.js +++ b/erpnext/accounts/doctype/budget/budget.js @@ -3,14 +3,6 @@ frappe.ui.form.on('Budget', { onload: function(frm) { - frm.set_query("project", function() { - return { - filters: { - company: frm.doc.company - } - } - }) - frm.set_query("account", "accounts", function() { return { filters: { diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js index 96006158b6..bc34d6c3b1 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js @@ -112,15 +112,7 @@ frappe.ui.form.on('Payroll Entry', { "company": frm.doc.company } }; - }), - - frm.set_query("project", function () { - return { - filters: { - company: frm.doc.company - } - }; - }); + }) }, payroll_frequency: function (frm) { From 9e9ea965824c8562d915d3983641b0df1bafec15 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Nov 2020 12:03:47 +0530 Subject: [PATCH 085/728] fix: linting --- .../accounting_dimension_filter.js | 10 +++++----- .../accounting_dimension_filter.py | 1 - erpnext/payroll/doctype/payroll_entry/payroll_entry.js | 2 +- erpnext/public/js/queries.js | 2 +- erpnext/public/js/utils/dimension_tree_filter.js | 9 ++++----- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js index 6c254fcfb7..3e880d3ca6 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js @@ -5,13 +5,13 @@ frappe.ui.form.on('Accounting Dimension Filter', { onload: function(frm) { frappe.db.get_list('Accounting Dimension', {fields: ['name']}).then((res) => { - let options = ['Cost Center', 'Project']; + let options = ['Cost Center', 'Project']; - res.forEach((dimension) => { - options.push(dimension.name); - }); + res.forEach((dimension) => { + options.push(dimension.name); + }); - frm.set_df_property('accounting_dimension', 'options', options); + frm.set_df_property('accounting_dimension', 'options', options); }); }, diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py index ccfafd96ed..0dcf116423 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py @@ -44,7 +44,6 @@ def get_dimension_filter_map(): """, as_dict=1) dimension_filter_map = {} - account_filter_map = {} for f in filters: if f.accounting_dimension in ('Cost Center', 'Project'): diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js index bc34d6c3b1..d32fdbcaf1 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js @@ -112,7 +112,7 @@ frappe.ui.form.on('Payroll Entry', { "company": frm.doc.company } }; - }) + }); }, payroll_frequency: function (frm) { diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js index 7b7a9df1ac..98f1b504cc 100644 --- a/erpnext/public/js/queries.js +++ b/erpnext/public/js/queries.js @@ -134,7 +134,7 @@ $.extend(erpnext.queries, { 'account': account, 'company': company } - } + }; } }); diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index 34b563553e..7a42fb5614 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -1,5 +1,4 @@ frappe.provide('frappe.ui.form'); - let default_dimensions = {}; let doctypes_with_dimensions = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset", @@ -34,7 +33,7 @@ doctypes_with_dimensions.forEach((doctype) => { parent_fields.push(df.fieldname); } else if (df.fieldtype === 'Table') { setup_child_filters(frm, df.options, df.fieldname, dimension['fieldname']); - }; + } setup_account_filters(frm, dimension['fieldname'], parent_fields); }); @@ -90,7 +89,7 @@ let copy_dimension = function(frm, cdt, cdn, fieldname) { let row = frappe.get_doc(cdt, cdn); frm.script_manager.copy_from_first_row(fieldname, row, [dimension['fieldname']]); }); -} +}; let setup_child_filters = function(frm, doctype, parentfield, dimension) { let fields = []; @@ -107,10 +106,10 @@ let setup_child_filters = function(frm, doctype, parentfield, dimension) { return erpnext.queries.get_filtered_dimensions(row, fields, dimension, doc.company); }); }); -} +}; let setup_account_filters = function(frm, dimension, fields) { frm.set_query(dimension, function(doc) { return erpnext.queries.get_filtered_dimensions(doc, fields, dimension, doc.company); }); -} \ No newline at end of file +}; \ No newline at end of file From d51c953c22a2de6824fbe88e5d9989db19220d1b Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 16 Nov 2020 12:48:40 +0530 Subject: [PATCH 086/728] chore: Fix Tests, use project name instead of project_name field --- .../accounts/doctype/budget/test_budget.py | 15 +++++++--- .../journal_entry/test_journal_entry.py | 23 +++++++++------ .../purchase_invoice/test_purchase_invoice.py | 29 +++++++++++-------- .../sales_invoice/test_sales_invoice.py | 14 ++++----- .../doctype/crop_cycle/test_crop_cycle.py | 2 +- .../test_employee_onboarding.py | 3 +- .../expense_claim/test_expense_claim.py | 17 ++++++----- .../projects/doctype/project/test_project.py | 4 +++ erpnext/projects/doctype/task/test_task.py | 8 +++-- .../doctype/timesheet/test_timesheet.py | 5 ++-- 10 files changed, 73 insertions(+), 47 deletions(-) diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py index 0f115f9cc2..62d17c4e69 100644 --- a/erpnext/accounts/doctype/budget/test_budget.py +++ b/erpnext/accounts/doctype/budget/test_budget.py @@ -122,8 +122,10 @@ class TestBudget(unittest.TestCase): frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") + project = frappe.get_value("Project", {"project_name": "_Test Project"}) + jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", - "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project="_Test Project", posting_date=nowdate()) + "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project=project, posting_date=nowdate()) self.assertRaises(BudgetError, jv.submit) @@ -147,8 +149,11 @@ class TestBudget(unittest.TestCase): budget = make_budget(budget_against="Project") + project = frappe.get_value("Project", {"project_name": "_Test Project"}) + jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", - "_Test Bank - _TC", 250000, "_Test Cost Center - _TC", project="_Test Project", posting_date=nowdate()) + "_Test Bank - _TC", 250000, "_Test Cost Center - _TC", + project=project, posting_date=nowdate()) self.assertRaises(BudgetError, jv.submit) @@ -184,9 +189,11 @@ class TestBudget(unittest.TestCase): if month > 10: month = 10 + project = frappe.get_value("Project", {"project_name": "_Test Project"}) for i in range(month): jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", - "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True, project="_Test Project") + "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True, + project=project) self.assertTrue(frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})) @@ -289,7 +296,7 @@ def make_budget(**args): budget = frappe.new_doc("Budget") if budget_against == "Project": - budget.project = "_Test Project" + budget.project = frappe.get_value("Project", {"project_name": "_Test Project"}) else: budget.cost_center =cost_center or "_Test Cost Center - _TC" diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 53c07583d8..402ea5085e 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -168,7 +168,7 @@ class TestJournalEntry(unittest.TestCase): self.assertFalse(gle) def test_reverse_journal_entry(self): - from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry + from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry jv = make_journal_entry("_Test Bank USD - _TC", "Sales - _TC", 100, exchange_rate=50, save=False) @@ -307,15 +307,20 @@ class TestJournalEntry(unittest.TestCase): def test_jv_with_project(self): from erpnext.projects.doctype.project.test_project import make_project - project = make_project({ - 'project_name': 'Journal Entry Project', - 'project_template_name': 'Test Project Template', - 'start_date': '2020-01-01' - }) + + if not frappe.db.exists("Project", {"project_name": "Journal Entry Project"}): + project = make_project({ + 'project_name': 'Journal Entry Project', + 'project_template_name': 'Test Project Template', + 'start_date': '2020-01-01' + }) + project_name = project.name + else: + project_name = frappe.get_value("Project", {"project_name": "_Test Project"}) jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, save=False) for d in jv.accounts: - d.project = project.project_name + d.project = project_name jv.voucher_type = "Bank Entry" jv.multi_currency = 0 jv.cheque_no = "112233" @@ -325,10 +330,10 @@ class TestJournalEntry(unittest.TestCase): expected_values = { "_Test Cash - _TC": { - "project": project.project_name + "project": project_name }, "_Test Bank - _TC": { - "project": project.project_name + "project": project_name } } diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 2e5a7142a3..26acad3a4f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -436,26 +436,31 @@ class TestPurchaseInvoice(unittest.TestCase): ) def test_total_purchase_cost_for_project(self): - make_project({'project_name':'_Test Project'}) + if not frappe.db.exists("Project", {"project_name": "_Test Project for Purchase"}): + project = make_project({'project_name':'_Test Project for Purchase'}) + else: + project = frappe.get_doc("Project", {"project_name": "_Test Project for Purchase"}) existing_purchase_cost = frappe.db.sql("""select sum(base_net_amount) - from `tabPurchase Invoice Item` where project = '_Test Project' and docstatus=1""") + from `tabPurchase Invoice Item` + where project = '{0}' + and docstatus=1""".format(project.name)) existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0 - pi = make_purchase_invoice(currency="USD", conversion_rate=60, project="_Test Project") - self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), + pi = make_purchase_invoice(currency="USD", conversion_rate=60, project=project.name) + self.assertEqual(frappe.db.get_value("Project", project.name, "total_purchase_cost"), existing_purchase_cost + 15000) - pi1 = make_purchase_invoice(qty=10, project="_Test Project") - self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), + pi1 = make_purchase_invoice(qty=10, project=project.name) + self.assertEqual(frappe.db.get_value("Project", project.name, "total_purchase_cost"), existing_purchase_cost + 15500) pi1.cancel() - self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), + self.assertEqual(frappe.db.get_value("Project", project.name, "total_purchase_cost"), existing_purchase_cost + 15000) pi.cancel() - self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), existing_purchase_cost) + self.assertEqual(frappe.db.get_value("Project", project.name, "total_purchase_cost"), existing_purchase_cost) def test_return_purchase_invoice(self): set_perpetual_inventory() @@ -874,17 +879,17 @@ class TestPurchaseInvoice(unittest.TestCase): }) pi = make_purchase_invoice(credit_to="Creditors - _TC" ,do_not_save=1) - pi.items[0].project = item_project.project_name - pi.project = project.project_name + pi.items[0].project = item_project.name + pi.project = project.name pi.submit() expected_values = { "Creditors - _TC": { - "project": project.project_name + "project": project.name }, "_Test Account Cost for Goods Sold - _TC": { - "project": item_project.project_name + "project": item_project.name } } diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 9660c9570e..29b1ea2f9f 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1571,7 +1571,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 @@ -1587,17 +1587,17 @@ class TestSalesInvoice(unittest.TestCase): }) sales_invoice = create_sales_invoice(do_not_save=1) - sales_invoice.items[0].project = item_project.project_name - sales_invoice.project = project.project_name + sales_invoice.items[0].project = item_project.name + sales_invoice.project = project.name sales_invoice.submit() expected_values = { "Debtors - _TC": { - "project": project.project_name + "project": project.name }, "Sales - _TC": { - "project": item_project.project_name + "project": item_project.name } } @@ -1605,9 +1605,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) diff --git a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py b/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py index 5510d5ac02..763b4036c3 100644 --- a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py +++ b/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py @@ -71,4 +71,4 @@ def check_task_creation(): def check_project_creation(): - return True if frappe.db.exists('Project', 'Basil from seed 2017') else False + return True if frappe.db.exists('Project', {'project_name': 'Basil from seed 2017'}) else False diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py index 4e9ee3b143..336e13c9b7 100644 --- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py +++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py @@ -38,7 +38,8 @@ class TestEmployeeOnboarding(unittest.TestCase): onboarding.insert() onboarding.submit() - self.assertEqual(onboarding.project, 'Employee Onboarding : Test Researcher - test@researcher.com') + project_name = frappe.db.get_value("Project", onboarding.project, "project_name") + self.assertEqual(project_name, 'Employee Onboarding : Test Researcher - test@researcher.com') # don't allow making employee if onboarding is not complete self.assertRaises(IncompleteTaskError, make_employee, onboarding.name) diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index 6e97f0513d..d9b472cce7 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -19,35 +19,36 @@ class TestExpenseClaim(unittest.TestCase): frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """) frappe.db.sql("update `tabExpense Claim` set project = '', task = ''") - frappe.get_doc({ + project = frappe.get_doc({ "project_name": "_Test Project 1", "doctype": "Project" - }).save() + }) + project.save() task = frappe.get_doc(dict( doctype = 'Task', subject = '_Test Project Task 1', status = 'Open', - project = '_Test Project 1' + project = project.name )).insert() task_name = task.name payable_account = get_payable_account(company_name) - make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", "_Test Project 1", task_name) + make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", project.name, task_name) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) - self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200) + self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 200) - expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4","_Test Project 1", task_name) + expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4", project.name, task_name) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700) - self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700) + self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 700) expense_claim2.cancel() self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) - self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200) + self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 200) def test_expense_claim_status(self): payable_account = get_payable_account(company_name) diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py index 0c4f6f1bdf..f31225e36b 100644 --- a/erpnext/projects/doctype/project/test_project.py +++ b/erpnext/projects/doctype/project/test_project.py @@ -47,6 +47,10 @@ def get_project(name): def make_project(args): args = frappe._dict(args) + + if args.project_name and frappe.db.exists("Project", {"project_name": args.project_name}): + return frappe.get_doc("Project", {"project_name": args.project_name}) + if args.project_template_name: template = make_project_template(args.project_template_name) else: diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py index 47a28fd111..6ad8a19532 100644 --- a/erpnext/projects/doctype/task/test_task.py +++ b/erpnext/projects/doctype/task/test_task.py @@ -30,14 +30,16 @@ class TestTask(unittest.TestCase): }) def test_reschedule_dependent_task(self): + project = frappe.get_value("Project", {"project_name": "_Test Project"}) + task1 = create_task("_Test Task 1", nowdate(), add_days(nowdate(), 10)) task2 = create_task("_Test Task 2", add_days(nowdate(), 11), add_days(nowdate(), 15), task1.name) - task2.get("depends_on")[0].project = "_Test Project" + task2.get("depends_on")[0].project = project task2.save() task3 = create_task("_Test Task 3", add_days(nowdate(), 11), add_days(nowdate(), 15), task2.name) - task3.get("depends_on")[0].project = "_Test Project" + task3.get("depends_on")[0].project = project task3.save() task1.update({ @@ -104,7 +106,7 @@ def create_task(subject, start=None, end=None, depends_on=None, project=None, sa task.subject = subject task.exp_start_date = start or nowdate() task.exp_end_date = end or nowdate() - task.project = project or "_Test Project" + task.project = project or frappe.get_value("Project", {"project_name": "_Test Project"}) if save: task.save() else: diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py index a5ce44dcf2..4cb38049ff 100644 --- a/erpnext/projects/doctype/timesheet/test_timesheet.py +++ b/erpnext/projects/doctype/timesheet/test_timesheet.py @@ -89,10 +89,11 @@ class TestTimesheet(unittest.TestCase): def test_timesheet_billing_based_on_project(self): emp = make_employee("test_employee_6@salary.com") + project = frappe.get_value("Project", {"project_name": "_Test Project"}) - timesheet = make_timesheet(emp, simulate=True, billable=1, project = '_Test Project', company='_Test Company') + timesheet = make_timesheet(emp, simulate=True, billable=1, project=project, company='_Test Company') sales_invoice = create_sales_invoice(do_not_save=True) - sales_invoice.project = '_Test Project' + sales_invoice.project = project sales_invoice.submit() ts = frappe.get_doc('Timesheet', timesheet.name) From 0c6319194e7fb498e9639d6efb66cbf1a629df89 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Nov 2020 13:20:19 +0530 Subject: [PATCH 087/728] fix: GL Entry validation for dimensions --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index def9ed6803..1ac607940f 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -13,6 +13,8 @@ from erpnext.accounts.utils import get_account_currency from erpnext.accounts.utils import get_fiscal_year from erpnext.exceptions import InvalidAccountCurrency from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_checks_for_pl_and_bs_accounts +from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import get_dimension_filter_map +from six import iteritems exclude_from_linked_with = True class GLEntry(Document): @@ -37,6 +39,7 @@ class GLEntry(Document): def on_update_with_args(self, adv_adj, update_outstanding = 'Yes'): self.validate_account_details(adv_adj) self.validate_dimensions_for_pl_and_bs() + self.validate_allowed_dimensions() validate_frozen_account(self.account, adv_adj) validate_balance_type(self.account, adv_adj) @@ -91,6 +94,21 @@ class GLEntry(Document): frappe.throw(_("Accounting Dimension {0} is required for 'Balance Sheet' account {1}.") .format(dimension.label, self.account)) + def validate_allowed_dimensions(self): + dimension_filter_map = get_dimension_filter_map() + for key, value in iteritems(dimension_filter_map): + dimension = key[0] + account = key[1] + + if self.account == account: + if value['allow_or_restrict'] == 'Allow': + if self.get(dimension) and self.get(dimension) not in value['allowed_dimensions']: + frappe.throw(_("Invalid value {0} for account {1}").format( + frappe.bold(self.get(dimension)), frappe.bold(self.account))) + else: + if self.get(dimension) and self.get(dimension) in value['allowed_dimensions']: + frappe.throw(_("Invalid value {0} for account {1}").format( + frappe.bold(self.get(dimension)), frappe.bold(self.account))) def check_pl_account(self): if self.is_opening=='Yes' and \ From d82c0f3beafe43146f396dce9593edd4a9d69557 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Nov 2020 20:32:16 +0530 Subject: [PATCH 088/728] fix: Add disable and mandatory check for accounting dimension filters --- .../accounting_dimension_filter.js | 37 ++++++++++++++++++- .../accounting_dimension_filter.json | 23 +++++++++++- .../accounting_dimension_filter.py | 8 ++-- .../allowed_dimension/allowed_dimension.json | 3 +- .../applicable_on_account.json | 15 +++++++- erpnext/accounts/doctype/gl_entry/gl_entry.py | 6 ++- 6 files changed, 80 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js index 3e880d3ca6..c8c32d58bd 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js @@ -3,16 +3,48 @@ frappe.ui.form.on('Accounting Dimension Filter', { onload: function(frm) { + frm.set_query('applicable_on_account', 'accounts', function() { + return { + filters : { + 'company': frm.doc.company + } + } + }); + frappe.db.get_list('Accounting Dimension', - {fields: ['name']}).then((res) => { + {fields: ['document_type']}).then((res) => { let options = ['Cost Center', 'Project']; res.forEach((dimension) => { - options.push(dimension.name); + options.push(dimension.document_type); }); frm.set_df_property('accounting_dimension', 'options', options); }); + + frm.trigger('setup_filters'); + }, + + setup_filters: function(frm) { + let filters = {}; + + frappe.model.with_doctype(frm.doc.accounting_dimension, function() { + if (frm.doc.accounting_dimension) { + if (frappe.model.is_tree(frm.doc.accounting_dimension)) { + filters['is_group'] = 0; + } + + if (frappe.meta.has_field(frm.doc.accounting_dimension, 'company')) { + filters['company'] = frm.doc.company; + } + + frm.set_query('dimension_value', 'dimensions', function() { + return { + filters: filters + } + }); + } + }); }, accounting_dimension: function(frm) { @@ -20,6 +52,7 @@ frappe.ui.form.on('Accounting Dimension Filter', { let row = frm.add_child("dimensions"); row.accounting_dimension = frm.doc.accounting_dimension; frm.refresh_field("dimensions"); + frm.trigger('setup_filters'); }, }); diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json index e626a09ce0..c1190a395f 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json @@ -7,8 +7,10 @@ "engine": "InnoDB", "field_order": [ "accounting_dimension", - "column_break_2", "allow_or_restrict", + "column_break_2", + "company", + "disabled", "section_break_4", "accounts", "column_break_6", @@ -70,11 +72,28 @@ "reqd": 1, "show_days": 1, "show_seconds": 1 + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2020-11-14 18:02:02.616932", + "modified": "2020-11-16 17:27:40.292860", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting Dimension Filter", diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py index 0dcf116423..210b2c8ea8 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py @@ -32,12 +32,13 @@ def get_dimension_filter_map(): filters = frappe.db.sql( """ SELECT a.applicable_on_account, d.dimension_value, p.accounting_dimension, - p.allow_or_restrict, ad.fieldname + p.allow_or_restrict, ad.fieldname, a.is_mandatory FROM `tabApplicable On Account` a, `tabAllowed Dimension` d, `tabAccounting Dimension Filter` p, `tabAccounting Dimension` ad WHERE p.name = a.parent + AND p.disabled = 0 AND p.name = d.parent AND (p.accounting_dimension = ad.name OR p.accounting_dimension in ('Cost Center', 'Project')) @@ -50,13 +51,14 @@ def get_dimension_filter_map(): f.fieldname = scrub(f.accounting_dimension) build_map(dimension_filter_map, f.fieldname, f.applicable_on_account, f.dimension_value, - f.allow_or_restrict) + f.allow_or_restrict, f.is_mandatory) return dimension_filter_map -def build_map(map_object, dimension, account, filter_value, allow_or_restrict): +def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory): map_object.setdefault((dimension, account), { 'allowed_dimensions': [], + 'is_mandatory': is_mandatory, 'allow_or_restrict': allow_or_restrict }) map_object[(dimension, account)]['allowed_dimensions'].append(filter_value) diff --git a/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json index 20024b0322..c2d34b3b7e 100644 --- a/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json +++ b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json @@ -22,6 +22,7 @@ "fieldname": "dimension_value", "fieldtype": "Dynamic Link", "in_list_view": 1, + "label": "Applicable Dimension", "options": "accounting_dimension", "show_days": 1, "show_seconds": 1 @@ -30,7 +31,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-11-14 19:54:03.269016", + "modified": "2020-11-16 17:41:50.422843", "modified_by": "Administrator", "module": "Accounts", "name": "Allowed Dimension", diff --git a/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json index 8305da2ba0..5c809515c2 100644 --- a/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json +++ b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json @@ -5,7 +5,8 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "applicable_on_account" + "applicable_on_account", + "is_mandatory" ], "fields": [ { @@ -17,12 +18,22 @@ "reqd": 1, "show_days": 1, "show_seconds": 1 + }, + { + "columns": 2, + "default": "0", + "fieldname": "is_mandatory", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Mandatory", + "show_days": 1, + "show_seconds": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-11-14 16:54:06.756883", + "modified": "2020-11-16 13:36:59.129672", "modified_by": "Administrator", "module": "Accounts", "name": "Applicable On Account", diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 1ac607940f..b3caf6a82e 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -77,11 +77,9 @@ class GLEntry(Document): .format(self.voucher_type, self.voucher_no, self.account)) def validate_dimensions_for_pl_and_bs(self): - account_type = frappe.db.get_value("Account", self.account, "report_type") for dimension in get_checks_for_pl_and_bs_accounts(): - if account_type == "Profit and Loss" \ and self.company == dimension.company and dimension.mandatory_for_pl and not dimension.disabled: if not self.get(dimension.fieldname): @@ -101,6 +99,10 @@ class GLEntry(Document): account = key[1] if self.account == account: + if value['is_mandatory'] and not self.get(dimension): + frappe.throw(_("{0} is mandatory for account {1}").format( + frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account))) + if value['allow_or_restrict'] == 'Allow': if self.get(dimension) and self.get(dimension) not in value['allowed_dimensions']: frappe.throw(_("Invalid value {0} for account {1}").format( From 4bd52b48424c2fbe02d5e00ca5ddd3ce4665eb44 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Nov 2020 23:01:36 +0530 Subject: [PATCH 089/728] fix: Add test case --- .../test_accounting_dimension.py | 64 ++++++++-------- .../accounting_dimension_filter.py | 10 +-- .../test_accounting_dimension_filter.py | 75 ++++++++++++++++++- 3 files changed, 110 insertions(+), 39 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py index 104880f6f3..b5375e106f 100644 --- a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py @@ -11,37 +11,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import d class TestAccountingDimension(unittest.TestCase): def setUp(self): - frappe.set_user("Administrator") - - if not frappe.db.exists("Accounting Dimension", {"document_type": "Department"}): - dimension = frappe.get_doc({ - "doctype": "Accounting Dimension", - "document_type": "Department", - }).insert() - else: - dimension1 = frappe.get_doc("Accounting Dimension", "Department") - dimension1.disabled = 0 - dimension1.save() - - if not frappe.db.exists("Accounting Dimension", {"document_type": "Location"}): - dimension1 = frappe.get_doc({ - "doctype": "Accounting Dimension", - "document_type": "Location", - }) - - dimension1.append("dimension_defaults", { - "company": "_Test Company", - "reference_document": "Location", - "default_dimension": "Block 1", - "mandatory_for_bs": 1 - }) - - dimension1.insert() - dimension1.save() - else: - dimension1 = frappe.get_doc("Accounting Dimension", "Location") - dimension1.disabled = 0 - dimension1.save() + create_dimension() def test_dimension_against_sales_invoice(self): si = create_sales_invoice(do_not_save=1) @@ -101,6 +71,38 @@ class TestAccountingDimension(unittest.TestCase): def tearDown(self): disable_dimension() +def create_dimension(): + frappe.set_user("Administrator") + + if not frappe.db.exists("Accounting Dimension", {"document_type": "Department"}): + dimension = frappe.get_doc({ + "doctype": "Accounting Dimension", + "document_type": "Department", + }).insert() + else: + dimension1 = frappe.get_doc("Accounting Dimension", "Department") + dimension1.disabled = 0 + dimension1.save() + + if not frappe.db.exists("Accounting Dimension", {"document_type": "Location"}): + dimension1 = frappe.get_doc({ + "doctype": "Accounting Dimension", + "document_type": "Location", + }) + + dimension1.append("dimension_defaults", { + "company": "_Test Company", + "reference_document": "Location", + "default_dimension": "Block 1", + "mandatory_for_bs": 1 + }) + + dimension1.insert() + dimension1.save() + else: + dimension1 = frappe.get_doc("Accounting Dimension", "Location") + dimension1.disabled = 0 + dimension1.save() def disable_dimension(): dimension1 = frappe.get_doc("Accounting Dimension", "Department") diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py index 210b2c8ea8..440073b723 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py @@ -32,23 +32,21 @@ def get_dimension_filter_map(): filters = frappe.db.sql( """ SELECT a.applicable_on_account, d.dimension_value, p.accounting_dimension, - p.allow_or_restrict, ad.fieldname, a.is_mandatory + p.allow_or_restrict, a.is_mandatory FROM `tabApplicable On Account` a, `tabAllowed Dimension` d, - `tabAccounting Dimension Filter` p, `tabAccounting Dimension` ad + `tabAccounting Dimension Filter` p WHERE p.name = a.parent AND p.disabled = 0 AND p.name = d.parent - AND (p.accounting_dimension = ad.name - OR p.accounting_dimension in ('Cost Center', 'Project')) + """, as_dict=1) dimension_filter_map = {} for f in filters: - if f.accounting_dimension in ('Cost Center', 'Project'): - f.fieldname = scrub(f.accounting_dimension) + f.fieldname = scrub(f.accounting_dimension) build_map(dimension_filter_map, f.fieldname, f.applicable_on_account, f.dimension_value, f.allow_or_restrict, f.is_mandatory) diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py index c271a25fbd..feb0af1bd5 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py @@ -3,8 +3,79 @@ # See license.txt from __future__ import unicode_literals -# import frappe +import frappe import unittest +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import create_dimension class TestAccountingDimensionFilter(unittest.TestCase): - pass + def setUp(self): + create_accounting_dimension_filter() + + def test_allowed_dimension_validation(self): + si = create_sales_invoice(do_not_save=1) + si.items[0].cost_center = 'Main - _TC' + si.save() + + self.assertRaises(frappe.ValidationError, si.submit) + + def test_mandatory_dimension_validation(self): + si = create_sales_invoice(do_not_save=1) + si.items[0].location = '' + si.save() + + self.assertRaises(frappe.ValidationError, si.submit) + + def tearDown(self): + disable_dimension_filter() + +def create_accounting_dimension_filter(): + if not frappe.db.get_value('Accounting Dimension Filter', + {'accounting_dimension': 'Cost Center'}): + frappe.get_doc({ + 'doctype': 'Accounting Dimension Filter', + 'accounting_dimension': 'Cost Center', + 'allow_or_restrict': 'Allow', + 'company': '_Test Company', + 'accounts': [{ + 'applicable_on_account': 'Sales - _TC', + }], + 'dimensions': [{ + 'accounting_dimension': 'Cost Center', + 'dimension_value': '_Test Cost Center 3 - _TC' + }] + }).insert() + else: + doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Cost Center'}) + doc.disabled = 0 + doc.save() + + if not frappe.db.get_value('Accounting Dimension Filter', + {'accounting_dimension': 'Location'}): + frappe.get_doc({ + 'doctype': 'Accounting Dimension Filter', + 'accounting_dimension': 'Location', + 'allow_or_restrict': 'Allow', + 'company': '_Test Company', + 'accounts': [{ + 'applicable_on_account': 'Sales - _TC', + 'is_mandatory': 1 + }], + 'dimensions': [{ + 'accounting_dimension': 'Location', + 'dimension_value': 'Block 1' + }] + }).insert() + else: + doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Location'}) + doc.disabled = 0 + doc.save() + +def disable_dimension_filter(): + doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Cost Center'}) + doc.disabled = 0 + doc.save() + + doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Location'}) + doc.disabled = 0 + doc.save() From 6456c3dc244c0b294748707ed08e558826f3ab65 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 17 Nov 2020 11:10:00 +0530 Subject: [PATCH 090/728] fix: Linting and test cases --- .../accounting_dimension/test_accounting_dimension.py | 8 ++++---- .../accounting_dimension_filter.js | 4 ++-- .../test_accounting_dimension_filter.py | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py index b5375e106f..fc1d7e344a 100644 --- a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py @@ -75,14 +75,14 @@ def create_dimension(): frappe.set_user("Administrator") if not frappe.db.exists("Accounting Dimension", {"document_type": "Department"}): - dimension = frappe.get_doc({ + frappe.get_doc({ "doctype": "Accounting Dimension", "document_type": "Department", }).insert() else: - dimension1 = frappe.get_doc("Accounting Dimension", "Department") - dimension1.disabled = 0 - dimension1.save() + dimension = frappe.get_doc("Accounting Dimension", "Department") + dimension.disabled = 0 + dimension.save() if not frappe.db.exists("Accounting Dimension", {"document_type": "Location"}): dimension1 = frappe.get_doc({ diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js index c8c32d58bd..994ee44354 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js @@ -8,7 +8,7 @@ frappe.ui.form.on('Accounting Dimension Filter', { filters : { 'company': frm.doc.company } - } + }; }); frappe.db.get_list('Accounting Dimension', @@ -41,7 +41,7 @@ frappe.ui.form.on('Accounting Dimension Filter', { frm.set_query('dimension_value', 'dimensions', function() { return { filters: filters - } + }; }); } }); diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py index feb0af1bd5..02fd75e759 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py @@ -10,6 +10,7 @@ from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension imp class TestAccountingDimensionFilter(unittest.TestCase): def setUp(self): + create_dimension() create_accounting_dimension_filter() def test_allowed_dimension_validation(self): @@ -42,7 +43,7 @@ def create_accounting_dimension_filter(): }], 'dimensions': [{ 'accounting_dimension': 'Cost Center', - 'dimension_value': '_Test Cost Center 3 - _TC' + 'dimension_value': '_Test Cost Center 2 - _TC' }] }).insert() else: From 8b4d92d4fcc8ffbd92366dd63bd66122719156d2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 17 Nov 2020 13:19:29 +0530 Subject: [PATCH 091/728] fix: Disable filters after test --- .../test_accounting_dimension_filter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py index 02fd75e759..801786b6e9 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py @@ -74,9 +74,9 @@ def create_accounting_dimension_filter(): def disable_dimension_filter(): doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Cost Center'}) - doc.disabled = 0 + doc.disabled = 1 doc.save() doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Location'}) - doc.disabled = 0 + doc.disabled = 1 doc.save() From 350972ece4e80b66d02f7943acdc2cb13f277f6f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 17 Nov 2020 13:51:58 +0530 Subject: [PATCH 092/728] fix: Account filters --- .../accounting_dimension_filter.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js index 994ee44354..f0362d3140 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js @@ -28,8 +28,8 @@ frappe.ui.form.on('Accounting Dimension Filter', { setup_filters: function(frm) { let filters = {}; - frappe.model.with_doctype(frm.doc.accounting_dimension, function() { - if (frm.doc.accounting_dimension) { + if (frm.doc.accounting_dimension) { + frappe.model.with_doctype(frm.doc.accounting_dimension, function() { if (frappe.model.is_tree(frm.doc.accounting_dimension)) { filters['is_group'] = 0; } @@ -43,8 +43,8 @@ frappe.ui.form.on('Accounting Dimension Filter', { filters: filters }; }); - } - }); + }); + } }, accounting_dimension: function(frm) { From f9e4f6b57a238f6f391754a7a7392928d6061294 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 17 Nov 2020 16:34:05 +0530 Subject: [PATCH 093/728] feat: add templates for online store --- .../shopping_cart/web_template/__init__.py | 0 .../web_template/hero_slider/__init__.py | 0 .../web_template/hero_slider/hero_slider.html | 0 .../web_template/hero_slider/hero_slider.json | 142 ++++++++ .../web_template/item_card_group/__init__.py | 0 .../item_card_group/item_card_group.html | 0 .../item_card_group/item_card_group.json | 312 ++++++++++++++++++ .../web_template/product_card/__init__.py | 0 .../product_card/product_card.html | 0 .../product_card/product_card.json | 33 ++ .../product_category_cards/__init__.py | 0 .../product_category_cards.html | 0 .../product_category_cards.json | 95 ++++++ 13 files changed, 582 insertions(+) create mode 100644 erpnext/shopping_cart/web_template/__init__.py create mode 100644 erpnext/shopping_cart/web_template/hero_slider/__init__.py create mode 100644 erpnext/shopping_cart/web_template/hero_slider/hero_slider.html create mode 100644 erpnext/shopping_cart/web_template/hero_slider/hero_slider.json create mode 100644 erpnext/shopping_cart/web_template/item_card_group/__init__.py create mode 100644 erpnext/shopping_cart/web_template/item_card_group/item_card_group.html create mode 100644 erpnext/shopping_cart/web_template/item_card_group/item_card_group.json create mode 100644 erpnext/shopping_cart/web_template/product_card/__init__.py create mode 100644 erpnext/shopping_cart/web_template/product_card/product_card.html create mode 100644 erpnext/shopping_cart/web_template/product_card/product_card.json create mode 100644 erpnext/shopping_cart/web_template/product_category_cards/__init__.py create mode 100644 erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.html create mode 100644 erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.json diff --git a/erpnext/shopping_cart/web_template/__init__.py b/erpnext/shopping_cart/web_template/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/hero_slider/__init__.py b/erpnext/shopping_cart/web_template/hero_slider/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json new file mode 100644 index 0000000000..a8ce2cd3cb --- /dev/null +++ b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json @@ -0,0 +1,142 @@ +{ + "__unsaved": 1, + "creation": "2020-11-17 15:21:51.207221", + "docstatus": 0, + "doctype": "Web Template", + "fields": [ + { + "__unsaved": 1, + "fieldname": "slide_1_image", + "fieldtype": "Attach Image", + "label": "Image", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_1_title", + "fieldtype": "Data", + "label": "Title", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_1_subtitle", + "fieldtype": "Data", + "label": "Subtitle", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_1_primary_action_label", + "fieldtype": "Data", + "label": "Primary Action Label", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_1_primary_action", + "fieldtype": "Data", + "label": "Primary Action", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2", + "fieldtype": "Section Break", + "label": "Slide 2", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2_image", + "fieldtype": "Attach Image", + "label": "Image ", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2_title", + "fieldtype": "Data", + "label": "Title ", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2_subtitle", + "fieldtype": "Data", + "label": "Subtitle ", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2_primary_action_label", + "fieldtype": "Data", + "label": "Primary Action Label ", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2_primary_action", + "fieldtype": "Data", + "label": "Primary Action ", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3", + "fieldtype": "Section Break", + "label": "Slide 3", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_image", + "fieldtype": "Attach Image", + "label": "Image ", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_title", + "fieldtype": "Data", + "label": "Title ", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_subtitle", + "fieldtype": "Data", + "label": "Subtitle ", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_primary_action_label", + "fieldtype": "Data", + "label": "Primary Action Label ", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_primary_action", + "fieldtype": "Data", + "label": "Primary Action ", + "reqd": 0 + } + ], + "idx": 0, + "modified": "2020-11-17 15:23:48.584868", + "modified_by": "Administrator", + "module": "Shopping Cart", + "name": "Hero Slider", + "owner": "Administrator", + "standard": 1, + "template": "", + "type": "Section" +} \ No newline at end of file diff --git a/erpnext/shopping_cart/web_template/item_card_group/__init__.py b/erpnext/shopping_cart/web_template/item_card_group/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/item_card_group/item_card_group.json b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.json new file mode 100644 index 0000000000..f1a70a99ad --- /dev/null +++ b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.json @@ -0,0 +1,312 @@ +{ + "__unsaved": 1, + "creation": "2020-11-17 15:35:05.285322", + "docstatus": 0, + "doctype": "Web Template", + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "reqd": 1 + }, + { + "fieldname": "subtitle", + "fieldtype": "Data", + "label": "Subtitle", + "reqd": 0 + }, + { + "fieldname": "card_1", + "fieldtype": "Section Break", + "label": "Card 1", + "reqd": 0 + }, + { + "fieldname": "card_1_item", + "fieldtype": "Link", + "label": "Item", + "options": "Item", + "reqd": 0 + }, + { + "fieldname": "card_1_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_2", + "fieldtype": "Section Break", + "label": "Card 2", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_2_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_2_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_3", + "fieldtype": "Section Break", + "label": "Card 3", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_3_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_3_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_4", + "fieldtype": "Section Break", + "label": "Card 4", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_4_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_4_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_5", + "fieldtype": "Section Break", + "label": "Card 5", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_5_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_5_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_6", + "fieldtype": "Section Break", + "label": "Card 6", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_6_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_6_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_7", + "fieldtype": "Section Break", + "label": "Card 7", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_7_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_7_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_8", + "fieldtype": "Section Break", + "label": "Card 8", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_8_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_8_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_9", + "fieldtype": "Section Break", + "label": "Card 9", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_9_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_9_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_10", + "fieldtype": "Section Break", + "label": "Card 10", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_10_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_10_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_11", + "fieldtype": "Section Break", + "label": "Card 11", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_11_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_11_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_12", + "fieldtype": "Section Break", + "label": "Card 12", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_12_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_12_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + } + ], + "idx": 0, + "modified": "2020-11-17 16:37:46.325181", + "modified_by": "Administrator", + "module": "Shopping Cart", + "name": "Item Card Group", + "owner": "Administrator", + "standard": 1, + "template": "", + "type": "Section" +} \ No newline at end of file diff --git a/erpnext/shopping_cart/web_template/product_card/__init__.py b/erpnext/shopping_cart/web_template/product_card/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/product_card/product_card.html b/erpnext/shopping_cart/web_template/product_card/product_card.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/product_card/product_card.json b/erpnext/shopping_cart/web_template/product_card/product_card.json new file mode 100644 index 0000000000..1059c1b251 --- /dev/null +++ b/erpnext/shopping_cart/web_template/product_card/product_card.json @@ -0,0 +1,33 @@ +{ + "__unsaved": 1, + "creation": "2020-11-17 15:28:47.809342", + "docstatus": 0, + "doctype": "Web Template", + "fields": [ + { + "__unsaved": 1, + "fieldname": "item", + "fieldtype": "Link", + "label": "Item", + "options": "Item", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "featured", + "fieldtype": "Check", + "label": "Featured", + "options": "", + "reqd": 0 + } + ], + "idx": 0, + "modified": "2020-11-17 15:33:34.982515", + "modified_by": "Administrator", + "module": "Shopping Cart", + "name": "Product Card", + "owner": "Administrator", + "standard": 1, + "template": "", + "type": "Component" +} \ No newline at end of file diff --git a/erpnext/shopping_cart/web_template/product_category_cards/__init__.py b/erpnext/shopping_cart/web_template/product_category_cards/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.html b/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.json b/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.json new file mode 100644 index 0000000000..1a3143c209 --- /dev/null +++ b/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.json @@ -0,0 +1,95 @@ +{ + "__unsaved": 1, + "creation": "2020-11-17 15:25:50.855934", + "docstatus": 0, + "doctype": "Web Template", + "fields": [ + { + "__unsaved": 1, + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "reqd": 1 + }, + { + "__unsaved": 1, + "fieldname": "subtitle", + "fieldtype": "Data", + "label": "Subtitle", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_1_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_2_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_3_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_4_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_5_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_6_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_7_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_8_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + } + ], + "idx": 0, + "modified": "2020-11-17 15:27:36.250460", + "modified_by": "Administrator", + "module": "Shopping Cart", + "name": "Product Category Cards", + "owner": "Administrator", + "standard": 1, + "template": "", + "type": "Section" +} \ No newline at end of file From 542bc017184db1187be0dcab948dbee0ba753a91 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 18 Nov 2020 15:00:34 +0530 Subject: [PATCH 094/728] 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 @@ + + """.format(item_link, frappe.bold(entry[1])) + + msg += """ +
{{ _("Sr") }} {{ _(tdf.label) }}
{{ d.idx }} -
{{ print_value(tdf, d, doc, visible_columns) }}
${__("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 8cf3bdd044aa26559ee6e7c4c9a10a5ac86a258f Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 18 Nov 2020 17:09:45 +0530 Subject: [PATCH 095/728] feat: add slider template --- .../web_template/hero_slider/hero_slider.html | 112 +++++++++ .../web_template/hero_slider/hero_slider.json | 230 ++++++++++++++++-- 2 files changed, 323 insertions(+), 19 deletions(-) diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html index e69de29bb2..e8b79743ab 100644 --- a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html +++ b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html @@ -0,0 +1,112 @@ +{%- macro slide(image, title, subtitle, action, label, index, align="Left", theme="Dark") -%} +{%- set align_class = resolve_class({ + 'text-right': align == 'Right', + 'text-centre': align == 'Center', + 'text-left': align == 'Left', +}) -%} + +{%- set heading_class = resolve_class({ + 'text-white': theme == 'Dark', + '': theme == 'Light', +}) -%} + +{%- endmacro -%} + + + + + + \ No newline at end of file diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json index a8ce2cd3cb..496fc7995e 100644 --- a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json +++ b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json @@ -5,35 +5,56 @@ "doctype": "Web Template", "fields": [ { - "__unsaved": 1, + "fieldname": "slider_name", + "fieldtype": "Data", + "label": "Slider Name", + "reqd": 1 + }, + { + "default": "1", + "fieldname": "show_indicators", + "fieldtype": "Check", + "label": "Show Indicators", + "reqd": 0 + }, + { + "default": "1", + "fieldname": "show_controls", + "fieldtype": "Check", + "label": "Show Controls", + "reqd": 0 + }, + { + "fieldname": "slide_1", + "fieldtype": "Section Break", + "label": "Slide 1", + "reqd": 0 + }, + { "fieldname": "slide_1_image", "fieldtype": "Attach Image", "label": "Image", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_1_title", "fieldtype": "Data", "label": "Title", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_1_subtitle", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Subtitle", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_1_primary_action_label", "fieldtype": "Data", "label": "Primary Action Label", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_1_primary_action", "fieldtype": "Data", "label": "Primary Action", @@ -41,46 +62,73 @@ }, { "__unsaved": 1, + "fieldname": "slide_1_content_align", + "fieldtype": "Select", + "label": "Content Align", + "options": "Left\nCentre\nRight", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_1_theme", + "fieldtype": "Select", + "label": "Slide Theme", + "options": "Dark\nLight", + "reqd": 0 + }, + { "fieldname": "slide_2", "fieldtype": "Section Break", "label": "Slide 2", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_2_image", "fieldtype": "Attach Image", "label": "Image ", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_2_title", "fieldtype": "Data", "label": "Title ", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_2_subtitle", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Subtitle ", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_2_primary_action_label", "fieldtype": "Data", "label": "Primary Action Label ", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_2_primary_action", "fieldtype": "Data", "label": "Primary Action ", "reqd": 0 }, + { + "__unsaved": 1, + "default": "Left", + "fieldname": "slide_2_content_align", + "fieldtype": "Select", + "label": "Content Align", + "options": "Left\nCentre\nRight", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2_theme", + "fieldtype": "Select", + "label": "Slide Theme", + "options": "Dark\nLight", + "reqd": 0 + }, { "__islocal": 1, "__unsaved": 1, @@ -94,7 +142,7 @@ "__unsaved": 1, "fieldname": "slide_3_image", "fieldtype": "Attach Image", - "label": "Image ", + "label": "Image", "reqd": 0 }, { @@ -102,15 +150,15 @@ "__unsaved": 1, "fieldname": "slide_3_title", "fieldtype": "Data", - "label": "Title ", + "label": "Title", "reqd": 0 }, { "__islocal": 1, "__unsaved": 1, "fieldname": "slide_3_subtitle", - "fieldtype": "Data", - "label": "Subtitle ", + "fieldtype": "Small Text", + "label": "Subtitle", "reqd": 0 }, { @@ -118,7 +166,7 @@ "__unsaved": 1, "fieldname": "slide_3_primary_action_label", "fieldtype": "Data", - "label": "Primary Action Label ", + "label": "Primary Action Label", "reqd": 0 }, { @@ -126,12 +174,156 @@ "__unsaved": 1, "fieldname": "slide_3_primary_action", "fieldtype": "Data", - "label": "Primary Action ", + "label": "Primary Action", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_content_align", + "fieldtype": "Select", + "label": "Content Align", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_theme", + "fieldtype": "Select", + "label": "Slide Theme", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4", + "fieldtype": "Section Break", + "label": "Slide 4", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_image", + "fieldtype": "Attach Image", + "label": "Image", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_title", + "fieldtype": "Data", + "label": "Title", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_subtitle", + "fieldtype": "Small Text", + "label": "Subtitle", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_primary_action_label", + "fieldtype": "Data", + "label": "Primary Action Label", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_primary_action", + "fieldtype": "Data", + "label": "Primary Action", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_content_align", + "fieldtype": "Select", + "label": "Content Align", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_theme", + "fieldtype": "Select", + "label": "Slide Theme", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5", + "fieldtype": "Section Break", + "label": "Slide 5", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_image", + "fieldtype": "Attach Image", + "label": "Image", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_title", + "fieldtype": "Data", + "label": "Title", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_subtitle", + "fieldtype": "Small Text", + "label": "Subtitle", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_primary_action_label", + "fieldtype": "Data", + "label": "Primary Action Label", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_primary_action", + "fieldtype": "Data", + "label": "Primary Action", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_content_align", + "fieldtype": "Select", + "label": "Content Align", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_theme", + "fieldtype": "Select", + "label": "Slide Theme", "reqd": 0 } ], "idx": 0, - "modified": "2020-11-17 15:23:48.584868", + "modified": "2020-11-18 17:09:31.323198", "modified_by": "Administrator", "module": "Shopping Cart", "name": "Hero Slider", From 3bec3cfef16f33945440b334a3fa98d851b73ed2 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 18 Nov 2020 17:10:02 +0530 Subject: [PATCH 096/728] fix: don't use primary action for create --- erpnext/stock/doctype/item/item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index faf4accc73..8b8b70031c 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -81,7 +81,7 @@ frappe.ui.form.on("Item", { }, __('Create')); } - frm.page.set_inner_btn_group_as_primary(__('Create')); + // frm.page.set_inner_btn_group_as_primary(__('Create')); } if (frm.doc.variant_of) { frm.set_intro(__('This Item is a Variant of {0} (Template).', From ca3809831cb7f2d423f7289ed2df1f39bd24b03a Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 18 Nov 2020 17:39:17 +0530 Subject: [PATCH 097/728] refactor: use h1 for slidetitle --- erpnext/shopping_cart/web_template/hero_slider/hero_slider.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html index e8b79743ab..3f0419e564 100644 --- a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html +++ b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html @@ -14,7 +14,7 @@ {%- if title or subtitle -%}
{0}{1}
+ + + + + {2} +
{0}{1}
+ """.format(_("Item"), _("Unassigned Qty"), formatted_item_rows) + frappe.msgprint(msg, title=_("Insufficient Capacity"), is_minimizable=True, wide=True) return updated_table if updated_table else items From ccbd432b56b952e7d40003c15202279379338336 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 24 Nov 2020 12:47:13 +0530 Subject: [PATCH 119/728] chore: Added Tests - Fixed Sider Issues - Added perms to Putaway Rule - Added Unit Tests to check warehouse assignment --- .../doctype/putaway_rule/putaway_rule.js | 4 +- .../doctype/putaway_rule/putaway_rule.json | 27 +- .../doctype/putaway_rule/putaway_rule.py | 7 +- .../doctype/putaway_rule/test_putaway_rule.py | 257 +++++++++++++++++- 4 files changed, 287 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.js b/erpnext/stock/doctype/putaway_rule/putaway_rule.js index 00a84b0e8d..e0569206ef 100644 --- a/erpnext/stock/doctype/putaway_rule/putaway_rule.js +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.js @@ -14,7 +14,7 @@ frappe.ui.form.on('Putaway Rule', { }, uom: function(frm) { - if(frm.doc.item_code && frm.doc.uom) { + if (frm.doc.item_code && frm.doc.uom) { return frm.call({ method: "erpnext.stock.get_item_details.get_conversion_factor", args: { @@ -22,7 +22,7 @@ frappe.ui.form.on('Putaway Rule', { uom: frm.doc.uom }, callback: function(r) { - if(!r.exc) { + if (!r.exc) { let stock_capacity = flt(frm.doc.capacity) * flt(r.message.conversion_factor); frm.set_value('conversion_factor', r.message.conversion_factor); frm.set_value('stock_capacity', stock_capacity); diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.json b/erpnext/stock/doctype/putaway_rule/putaway_rule.json index d5ae68faf3..e5b6b2b98f 100644 --- a/erpnext/stock/doctype/putaway_rule/putaway_rule.json +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.json @@ -107,7 +107,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2020-11-23 16:53:48.387054", + "modified": "2020-11-23 19:25:50.948068", "modified_by": "Administrator", "module": "Stock", "name": "Putaway Rule", @@ -121,7 +121,30 @@ "print": 1, "read": 1, "report": 1, - "role": "System Manager", + "role": "Stock Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager", "share": 1, "write": 1 } diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.py b/erpnext/stock/doctype/putaway_rule/putaway_rule.py index cc58def33a..73534aa14f 100644 --- a/erpnext/stock/doctype/putaway_rule/putaway_rule.py +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py @@ -17,6 +17,7 @@ class PutawayRule(Document): self.validate_warehouse_and_company() self.validate_capacity() self.validate_priority() + self.set_stock_capacity() def validate_duplicate_rule(self): existing_rule = frappe.db.exists("Putaway Rule", {"item_code": self.item_code, "warehouse": self.warehouse}) @@ -45,10 +46,13 @@ class PutawayRule(Document): if not self.capacity: frappe.throw(_("Capacity must be greater than 0"), title=_("Invalid")) + def set_stock_capacity(self): + self.stock_capacity = (flt(self.conversion_factor) or 1) * flt(self.capacity) + @frappe.whitelist() def get_ordered_putaway_rules(item_code, company): """Returns an ordered list of putaway rules to apply on an item.""" - rules = frappe.get_all("Putaway Rule", fields=["name", "stock_capacity", "priority", "warehouse"], + rules = frappe.get_all("Putaway Rule", fields=["name", "item_code", "stock_capacity", "priority", "warehouse"], filters={"item_code": item_code, "company": company, "disable": 0}, order_by="priority asc, capacity desc") @@ -86,6 +90,7 @@ def apply_putaway_rule(items, company): new_updated_table_row.name = '' new_updated_table_row.idx = 1 if not updated_table else flt(updated_table[-1].idx) + 1 new_updated_table_row.qty = to_allocate + new_updated_table_row.stock_qty = flt(to_allocate) * flt(new_updated_table_row.conversion_factor) new_updated_table_row.warehouse = warehouse updated_table.append(new_updated_table_row) diff --git a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py index e262217f84..7b81784d5f 100644 --- a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py +++ b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py @@ -2,9 +2,260 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt from __future__ import unicode_literals - -# import frappe +import frappe import unittest +from frappe.utils import add_days, nowdate +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.stock.get_item_details import get_conversion_factor +from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse +from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry +from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt +from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order class TestPutawayRule(unittest.TestCase): - pass + def setUp(self): + if not frappe.db.exists("Item", "_Rice"): + make_item("_Rice", { + 'is_stock_item': 1, + 'has_batch_no' : 1, + 'create_new_batch': 1, + 'stock_uom': 'Kg' + }) + + if not frappe.db.exists("Warehouse", {"warehouse_name": "Rack 1"}): + create_warehouse("Rack 1") + if not frappe.db.exists("Warehouse", {"warehouse_name": "Rack 2"}): + create_warehouse("Rack 2") + + if not frappe.db.exists("UOM", "Bag"): + new_uom = frappe.new_doc("UOM") + new_uom.uom_name = "Bag" + new_uom.save() + + def test_putaway_rules_priority(self): + """Test if rule is applied by priority, irrespective of free space.""" + warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) + warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) + + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=200, + uom="Kg") + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=300, + uom="Kg", priority=2) + + po = create_purchase_order(item_code="_Rice", qty=300) + self.assertEqual(len(po.items), 1) + + pr = make_purchase_receipt(po.name) + self.assertEqual(len(pr.items), 2) + self.assertEqual(pr.items[0].qty, 200) + self.assertEqual(pr.items[0].warehouse, warehouse_1) + self.assertEqual(pr.items[1].qty, 100) + self.assertEqual(pr.items[1].warehouse, warehouse_2) + + po.cancel() + rule_1.delete() + rule_2.delete() + + def test_putaway_rules_with_same_priority(self): + """Test if rule with more free space is applied, + among two rules with same priority and capacity.""" + warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) + warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) + + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=500, + uom="Kg") + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=500, + uom="Kg") + + # out of 500 kg capacity, occupy 100 kg in warehouse_1 + stock_receipt = make_stock_entry(item_code="_Rice", target=warehouse_1, qty=100, basic_rate=50) + + po = create_purchase_order(item_code="_Rice", qty=700) + self.assertEqual(len(po.items), 1) + + pr = make_purchase_receipt(po.name) + self.assertEqual(len(pr.items), 2) + self.assertEqual(pr.items[0].qty, 500) + # warehouse_2 has 500 kg free space, it is given priority + self.assertEqual(pr.items[0].warehouse, warehouse_2) + self.assertEqual(pr.items[1].qty, 200) + # warehouse_1 has 400 kg free space, it is given less priority + self.assertEqual(pr.items[1].warehouse, warehouse_1) + + po.cancel() + stock_receipt.cancel() + rule_1.delete() + rule_2.delete() + + def test_putaway_rules_with_insufficient_capacity(self): + """Test if qty exceeding capacity, is handled.""" + warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) + warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) + + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=100, + uom="Kg") + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=200, + uom="Kg") + + po = create_purchase_order(item_code="_Rice", qty=350) + self.assertEqual(len(po.items), 1) + + pr = make_purchase_receipt(po.name) + + self.assertEqual(len(pr.items), 3) + self.assertEqual(pr.items[0].qty, 200) + self.assertEqual(pr.items[0].warehouse, warehouse_2) + self.assertEqual(pr.items[1].qty, 100) + self.assertEqual(pr.items[1].warehouse, warehouse_1) + # extra qty has no warehouse assigned + self.assertEqual(pr.items[2].qty, 50) + self.assertEqual(pr.items[2].warehouse, '') + + po.cancel() + rule_1.delete() + rule_2.delete() + + def test_putaway_rules_multi_uom(self): + """Test rules applied on uom other than stock uom.""" + item = frappe.get_doc("Item", "_Rice") + if not frappe.db.get_value("UOM Conversion Detail", {"parent": "_Rice", "uom": "Bag"}): + item.append("uoms", { + "uom": "Bag", + "conversion_factor": 1000 + }) + item.save() + + warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) + warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) + + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=3, + uom="Bag") + self.assertEqual(rule_1.stock_capacity, 3000) + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=4, + uom="Bag") + self.assertEqual(rule_2.stock_capacity, 4000) + + stock_receipt = make_stock_entry(item_code="_Rice", target=warehouse_1, qty=1000, basic_rate=50) + + po = create_purchase_order(item_code="_Rice", qty=6, do_not_save=True) + po.items[0].uom = "Bag" + po.save() + po.submit() + + self.assertEqual(po.items[0].stock_qty, 6000) + + pr = make_purchase_receipt(po.name) + self.assertEqual(len(pr.items), 2) + self.assertEqual(pr.items[0].qty, 4) + self.assertEqual(pr.items[0].warehouse, warehouse_2) + self.assertEqual(pr.items[1].qty, 2) + self.assertEqual(pr.items[1].warehouse, warehouse_1) + + po.cancel() + stock_receipt.cancel() + rule_1.delete() + rule_2.delete() + + def test_putaway_rules_multi_uom_whole_uom(self): + """Test if whole UOMs are handled.""" + item = frappe.get_doc("Item", "_Rice") + if not frappe.db.get_value("UOM Conversion Detail", {"parent": "_Rice", "uom": "Bag"}): + item.append("uoms", { + "uom": "Bag", + "conversion_factor": 1000 + }) + item.save() + + frappe.db.set_value("UOM", "Bag", "must_be_whole_number", 1) + + warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) + warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) + + # Putaway Rule in different UOM + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=1, + uom="Bag") + self.assertEqual(rule_1.stock_capacity, 1000) + # Putaway Rule in Stock UOM + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=500) + self.assertEqual(rule_2.stock_capacity, 500) + # total capacity is 1500 Kg + + po = create_purchase_order(item_code="_Rice", qty=2, do_not_save=True) + # PO for 2 Bags (2000 Kg) + po.items[0].uom = "Bag" + po.save() + po.submit() + + self.assertEqual(po.items[0].stock_qty, 2000) + + pr = make_purchase_receipt(po.name) + self.assertEqual(len(pr.items), 2) + self.assertEqual(pr.items[0].qty, 1) + self.assertEqual(pr.items[0].warehouse, warehouse_1) + # leftover space was for 500 kg (0.5 Bag) + # Since Bag is a whole UOM, 1(out of 2) Bag will be unassigned + self.assertEqual(pr.items[1].qty, 1) + self.assertEqual(pr.items[1].warehouse, '') + + po.cancel() + rule_1.delete() + rule_2.delete() + + def test_putaway_rules_with_reoccurring_item(self): + """Test rules on same item entered multiple times.""" + warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) + warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) + + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=200, + uom="Kg") + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=100, + uom="Kg", priority=2) + # total capacity is 300 Kg + + po = create_purchase_order(item_code="_Rice", qty=200, rate=100, do_not_save=True) + po.append("items", { + "item_code":"_Rice", + "warehouse": "_Test Warehouse - _TC", + "qty": 300, + "rate": 120, + "schedule_date": add_days(nowdate(), 1), + }) + po.save() + po.submit() + # PO for 500 Kg (two rows of same item, different rates) + self.assertEqual(len(po.items), 2) + + pr = make_purchase_receipt(po.name) + self.assertEqual(len(pr.items), 3) + self.assertEqual(pr.items[0].qty, 200) + self.assertEqual(pr.items[0].warehouse, warehouse_1) + # same rules applied to second item row + # with previous assignment considered + self.assertEqual(pr.items[1].qty, 100) + self.assertEqual(pr.items[1].warehouse, warehouse_2) + # unassigned 200 Kg + self.assertEqual(pr.items[2].qty, 200) + self.assertEqual(pr.items[2].warehouse, '') + + po.cancel() + rule_1.delete() + rule_2.delete() + +def create_putaway_rule(**args): + args = frappe._dict(args) + putaway = frappe.new_doc("Putaway Rule") + + putaway.disable = args.disable or 0 + putaway.company = args.company or "_Test Company" + putaway.item_code = args.item or args.item_code or "_Test Item" + putaway.warehouse = args.warehouse + putaway.priority = args.priority or 1 + putaway.capacity = args.capacity or 1 + putaway.stock_uom = frappe.db.get_value("Item", putaway.item_code, "stock_uom") + putaway.uom = args.uom or putaway.stock_uom + putaway.conversion_factor = get_conversion_factor(putaway.item_code, putaway.uom)['conversion_factor'] + + if not args.do_not_save: + putaway.save() + + return putaway \ No newline at end of file From 7f1d7894c204b3dcadca47453c4556af2a72b86f Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 24 Nov 2020 14:04:49 +0530 Subject: [PATCH 120/728] 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 121/728] 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 68a49efc8098808386e234c380692791e926b2aa Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 24 Nov 2020 17:38:34 +0530 Subject: [PATCH 122/728] chore: Added Putaway Rule to Desk Page and added Priority to List View --- erpnext/stock/desk_page/stock/stock.json | 4 ++-- erpnext/stock/doctype/putaway_rule/putaway_rule.json | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 390fcd91e3..aa4fc28ec9 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 \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Putaway Rule\",\n \"name\": \"Putaway Rule\",\n \"onboard\": 1,\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-11-24 15:43:20.496057", "modified_by": "Administrator", "module": "Stock", "name": "Stock", diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.json b/erpnext/stock/doctype/putaway_rule/putaway_rule.json index e5b6b2b98f..325e6f1355 100644 --- a/erpnext/stock/doctype/putaway_rule/putaway_rule.json +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.json @@ -68,6 +68,7 @@ "default": "1", "fieldname": "priority", "fieldtype": "Int", + "in_list_view": 1, "label": "Priority" }, { @@ -107,7 +108,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2020-11-23 19:25:50.948068", + "modified": "2020-11-24 16:20:18.306671", "modified_by": "Administrator", "module": "Stock", "name": "Putaway Rule", From 2ed80656aa1be30fb735a85202ce71d62eba6763 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 24 Nov 2020 21:06:43 +0530 Subject: [PATCH 123/728] chore: Code Cleanup - Validate capacity < stock level only on new rule - Mention stock uom while validating capacity in new rule - Separate function to format and display unassigned items - Format ORM args --- .../doctype/putaway_rule/putaway_rule.py | 59 ++++++++++--------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.py b/erpnext/stock/doctype/putaway_rule/putaway_rule.py index 73534aa14f..606e190458 100644 --- a/erpnext/stock/doctype/putaway_rule/putaway_rule.py +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py @@ -38,10 +38,13 @@ class PutawayRule(Document): title=_("Invalid Warehouse")) def validate_capacity(self): + stock_uom = frappe.db.get_value("Item", self.item_code, "stock_uom") balance_qty = get_stock_balance(self.item_code, self.warehouse, nowdate()) - if flt(self.stock_capacity) < flt(balance_qty): - frappe.throw(_("Warehouse Capacity for Item '{0}' must be greater than the existing stock level of {1} qty.") - .format(self.item_code, frappe.bold(balance_qty)), title=_("Insufficient Capacity")) + + if flt(self.stock_capacity) < flt(balance_qty) and self.get('__islocal'): + frappe.throw(_("Warehouse Capacity for Item '{0}' must be greater than the existing stock level of {1} {2}.") + .format(self.item_code, frappe.bold(balance_qty), stock_uom), + title=_("Insufficient Capacity")) if not self.capacity: frappe.throw(_("Capacity must be greater than 0"), title=_("Invalid")) @@ -49,10 +52,10 @@ class PutawayRule(Document): def set_stock_capacity(self): self.stock_capacity = (flt(self.conversion_factor) or 1) * flt(self.capacity) -@frappe.whitelist() def get_ordered_putaway_rules(item_code, company): """Returns an ordered list of putaway rules to apply on an item.""" - rules = frappe.get_all("Putaway Rule", fields=["name", "item_code", "stock_capacity", "priority", "warehouse"], + rules = frappe.get_all("Putaway Rule", + fields=["name", "item_code", "stock_capacity", "priority", "warehouse"], filters={"item_code": item_code, "company": company, "disable": 0}, order_by="priority asc, capacity desc") @@ -145,27 +148,29 @@ def apply_putaway_rule(items, company): items_not_accomodated.append([item.item_code, pending_qty]) if items_not_accomodated: - msg = _("The following Items, having Putaway Rules, could not be accomodated:") + "

" - formatted_item_rows = "" - - for entry in items_not_accomodated: - item_link = frappe.utils.get_link_to_form("Item", entry[0]) - formatted_item_rows += """ - {0} - {1} - """.format(item_link, frappe.bold(entry[1])) - - msg += """ - - - - - - {2} -
{0}{1}
- """.format(_("Item"), _("Unassigned Qty"), formatted_item_rows) - - frappe.msgprint(msg, title=_("Insufficient Capacity"), is_minimizable=True, wide=True) + format_unassigned_items_error(items_not_accomodated) return updated_table if updated_table else items - # TODO: check pricing rule, item tax impact \ No newline at end of file + +def format_unassigned_items_error(items_not_accomodated): + msg = _("The following Items, having Putaway Rules, could not be accomodated:") + "

" + formatted_item_rows = "" + + for entry in items_not_accomodated: + item_link = frappe.utils.get_link_to_form("Item", entry[0]) + formatted_item_rows += """ + {0} + {1} + """.format(item_link, frappe.bold(entry[1])) + + msg += """ + + + + + + {2} +
{0}{1}
+ """.format(_("Item"), _("Unassigned Qty"), formatted_item_rows) + + frappe.msgprint(msg, title=_("Insufficient Capacity"), is_minimizable=True, wide=True) \ No newline at end of file From 6c17b84caef6300f3872ce5b5db031e0ec7501fd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 Nov 2020 13:42:16 +0530 Subject: [PATCH 124/728] fix: Replace raw query with ORM --- erpnext/controllers/queries.py | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 015807d563..e3aac9aba8 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -499,16 +499,17 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters) from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import get_dimension_filter_map dimension_filters = get_dimension_filter_map() dimension_filters = dimension_filters.get((filters.get('dimension'),filters.get('account'))) - group_condition = '' - company_condition = '' + query_filters = [] meta = frappe.get_meta(doctype) - if meta.is_tree: - group_condition = 'and is_group = 0 ' + query_filters.append(['is_group', '=', 0]) if meta.has_field('company'): - company_condition = 'and company = %s ' % (frappe.db.escape(filters.get('company'))) + query_filters.append(['company', '=', filters.get('company')]) + + if txt: + query_filters.append([searchfield, 'LIKE', "%%%s%%" % txt]) if dimension_filters: if dimension_filters['allow_or_restrict'] == 'Allow': @@ -521,25 +522,12 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters) else: dimensions = tuple(dimension_filters['allowed_dimensions']) - result = frappe.db.sql("""SELECT name from `tab{doctype}` where - name {query_selector} {restricted} - {group_condition} {company_condition} - and {key} LIKE %(txt)s""".format( - doctype=doctype, query_selector=query_selector, restricted=dimensions, - group_condition = group_condition, - company_condition = company_condition, - key=searchfield), { - 'txt': '%' + txt + '%' - }) + query_filters.append(['name', query_selector, dimensions]) - return result - else: - return frappe.db.sql(""" - SELECT name from `tab{doctype}` where - {key} LIKE %(txt)s {group_condition} {company_condition}""" - .format(doctype=doctype, key=searchfield, - group_condition=group_condition, company_condition=company_condition), - { 'txt': '%' + txt + '%'}) + output = frappe.get_all(doctype, filters=query_filters) + result = [d.name for d in output] + + return [(d,) for d in set(result)] @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs From df065f7044df675d161314992a2eaeec83971839 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 Nov 2020 13:44:52 +0530 Subject: [PATCH 125/728] fix: form layout and naming fixes --- .../accounting_dimension.js | 1 - .../accounting_dimension.py | 4 +-- .../accounting_dimension_filter.js | 7 +++++- .../accounting_dimension_filter.json | 10 ++++---- .../accounting_dimension_filter.py | 25 +++++++++---------- 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js index 9a6c389339..65c5ff1cea 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js @@ -2,7 +2,6 @@ // For license information, please see license.txt frappe.ui.form.on('Accounting Dimension', { - refresh: function(frm) { frm.set_query('document_type', () => { let invalid_doctypes = frappe.model.core_doctypes_list; diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index b9d4da289e..52e9ff8b76 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -203,7 +203,7 @@ def get_dimension_with_children(doctype, dimension): return all_dimensions @frappe.whitelist() -def get_dimension_filters(with_costcenter_and_project=False): +def get_dimensions(with_cost_center_and_project=False): dimension_filters = frappe.db.sql(""" SELECT label, fieldname, document_type FROM `tabAccounting Dimension` @@ -214,7 +214,7 @@ def get_dimension_filters(with_costcenter_and_project=False): FROM `tabAccounting Dimension Detail` c, `tabAccounting Dimension` p WHERE c.parent = p.name""", as_dict=1) - if with_costcenter_and_project: + if with_cost_center_and_project: dimension_filters.extend([ { 'fieldname': 'cost_center', diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js index f0362d3140..a2526e92c3 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js @@ -2,10 +2,15 @@ // For license information, please see license.txt frappe.ui.form.on('Accounting Dimension Filter', { + refresh: function(frm, cdt, cdn) { + if (frm.doc.accounting_dimension) { + frm.set_df_property('dimensions', 'label', frm.doc.accounting_dimension, cdn, 'dimension_value'); + } + }, onload: function(frm) { frm.set_query('applicable_on_account', 'accounts', function() { return { - filters : { + filters: { 'company': frm.doc.company } }; diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json index c1190a395f..7736b2dffb 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json @@ -7,10 +7,10 @@ "engine": "InnoDB", "field_order": [ "accounting_dimension", - "allow_or_restrict", + "disabled", "column_break_2", "company", - "disabled", + "allow_or_restrict", "section_break_4", "accounts", "column_break_6", @@ -57,7 +57,7 @@ { "fieldname": "accounts", "fieldtype": "Table", - "label": "Accounts", + "label": "Applicable On Account", "options": "Applicable On Account", "reqd": 1, "show_days": 1, @@ -67,7 +67,7 @@ "depends_on": "eval:doc.accounting_dimension", "fieldname": "dimensions", "fieldtype": "Table", - "label": "Dimensions", + "label": "Applicable Dimension", "options": "Allowed Dimension", "reqd": 1, "show_days": 1, @@ -93,7 +93,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2020-11-16 17:27:40.292860", + "modified": "2020-11-24 12:34:42.458713", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting Dimension Filter", diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py index 440073b723..6aef9caa74 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py @@ -29,19 +29,18 @@ class AccountingDimensionFilter(Document): account.idx, frappe.bold(account.applicable_on_account), frappe.bold(self.accounting_dimension))) def get_dimension_filter_map(): - filters = frappe.db.sql( - """ SELECT - a.applicable_on_account, d.dimension_value, p.accounting_dimension, - p.allow_or_restrict, a.is_mandatory - FROM - `tabApplicable On Account` a, `tabAllowed Dimension` d, - `tabAccounting Dimension Filter` p - WHERE - p.name = a.parent - AND p.disabled = 0 - AND p.name = d.parent - - """, as_dict=1) + filters = frappe.db.sql(""" + SELECT + a.applicable_on_account, d.dimension_value, p.accounting_dimension, + p.allow_or_restrict, a.is_mandatory + FROM + `tabApplicable On Account` a, `tabAllowed Dimension` d, + `tabAccounting Dimension Filter` p + WHERE + p.name = a.parent + AND p.disabled = 0 + AND p.name = d.parent + """, as_dict=1) dimension_filter_map = {} From 4e991fe668daafe8488560fddef806cbc540373d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 Nov 2020 13:46:07 +0530 Subject: [PATCH 126/728] fix: Define specific exceptions and fix tests --- .../test_accounting_dimension_filter.py | 27 ++++++++++++------- erpnext/accounts/doctype/gl_entry/gl_entry.py | 8 +++--- erpnext/exceptions.py | 2 ++ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py index 801786b6e9..f67e1de404 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py @@ -6,7 +6,8 @@ from __future__ import unicode_literals import frappe import unittest from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice -from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import create_dimension +from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import create_dimension, disable_dimension +from erpnext.exceptions import InvalidAccountDimension, MandatoryDimension class TestAccountingDimensionFilter(unittest.TestCase): def setUp(self): @@ -16,19 +17,25 @@ class TestAccountingDimensionFilter(unittest.TestCase): def test_allowed_dimension_validation(self): si = create_sales_invoice(do_not_save=1) si.items[0].cost_center = 'Main - _TC' + si.location = 'Block 1' si.save() - self.assertRaises(frappe.ValidationError, si.submit) + self.assertRaises(InvalidAccountDimension, si.submit) def test_mandatory_dimension_validation(self): si = create_sales_invoice(do_not_save=1) - si.items[0].location = '' + si.location = 'Block 1' + + # Test with no department for Sales Account + si.items[0].department = '' + si.items[0].cost_center = '_Test Cost Center 2 - _TC' si.save() - self.assertRaises(frappe.ValidationError, si.submit) + self.assertRaises(MandatoryDimension, si.submit) def tearDown(self): disable_dimension_filter() + disable_dimension() def create_accounting_dimension_filter(): if not frappe.db.get_value('Accounting Dimension Filter', @@ -52,10 +59,10 @@ def create_accounting_dimension_filter(): doc.save() if not frappe.db.get_value('Accounting Dimension Filter', - {'accounting_dimension': 'Location'}): + {'accounting_dimension': 'Department'}): frappe.get_doc({ 'doctype': 'Accounting Dimension Filter', - 'accounting_dimension': 'Location', + 'accounting_dimension': 'Department', 'allow_or_restrict': 'Allow', 'company': '_Test Company', 'accounts': [{ @@ -63,12 +70,12 @@ def create_accounting_dimension_filter(): 'is_mandatory': 1 }], 'dimensions': [{ - 'accounting_dimension': 'Location', - 'dimension_value': 'Block 1' + 'accounting_dimension': 'Department', + 'dimension_value': '_Test Department - _TC' }] }).insert() else: - doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Location'}) + doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Department'}) doc.disabled = 0 doc.save() @@ -77,6 +84,6 @@ def disable_dimension_filter(): doc.disabled = 1 doc.save() - doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Location'}) + doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Department'}) doc.disabled = 1 doc.save() diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index b3caf6a82e..f586de82e3 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -11,7 +11,7 @@ from frappe.model.meta import get_field_precision from erpnext.accounts.party import validate_party_gle_currency, validate_party_frozen_disabled from erpnext.accounts.utils import get_account_currency from erpnext.accounts.utils import get_fiscal_year -from erpnext.exceptions import InvalidAccountCurrency +from erpnext.exceptions import InvalidAccountCurrency, InvalidAccountDimension, MandatoryDimension from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_checks_for_pl_and_bs_accounts from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import get_dimension_filter_map from six import iteritems @@ -101,16 +101,16 @@ class GLEntry(Document): if self.account == account: if value['is_mandatory'] and not self.get(dimension): frappe.throw(_("{0} is mandatory for account {1}").format( - frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account))) + frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), MandatoryDimension) if value['allow_or_restrict'] == 'Allow': if self.get(dimension) and self.get(dimension) not in value['allowed_dimensions']: frappe.throw(_("Invalid value {0} for account {1}").format( - frappe.bold(self.get(dimension)), frappe.bold(self.account))) + frappe.bold(self.get(dimension)), frappe.bold(self.account)), InvalidAccountDimension) else: if self.get(dimension) and self.get(dimension) in value['allowed_dimensions']: frappe.throw(_("Invalid value {0} for account {1}").format( - frappe.bold(self.get(dimension)), frappe.bold(self.account))) + frappe.bold(self.get(dimension)), frappe.bold(self.account)), InvalidAccountDimension) def check_pl_account(self): if self.is_opening=='Yes' and \ diff --git a/erpnext/exceptions.py b/erpnext/exceptions.py index d92af5d722..dcf3d6bad1 100644 --- a/erpnext/exceptions.py +++ b/erpnext/exceptions.py @@ -6,3 +6,5 @@ class PartyFrozen(frappe.ValidationError): pass class InvalidAccountCurrency(frappe.ValidationError): pass class InvalidCurrency(frappe.ValidationError): pass class PartyDisabled(frappe.ValidationError):pass +class InvalidAccountDimension(frappe.ValidationError): pass +class MandatoryDimension(frappe.ValidationError): pass From 92b3449789689fc71dbd2ea3df7a2f7553cb0dec Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 Nov 2020 14:01:57 +0530 Subject: [PATCH 127/728] fix: Label changes --- .../accounts/doctype/allowed_dimension/allowed_dimension.json | 3 +-- .../doctype/applicable_on_account/applicable_on_account.json | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json index c2d34b3b7e..7fe2a3c647 100644 --- a/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json +++ b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json @@ -22,7 +22,6 @@ "fieldname": "dimension_value", "fieldtype": "Dynamic Link", "in_list_view": 1, - "label": "Applicable Dimension", "options": "accounting_dimension", "show_days": 1, "show_seconds": 1 @@ -31,7 +30,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-11-16 17:41:50.422843", + "modified": "2020-11-23 09:56:19.744200", "modified_by": "Administrator", "module": "Accounts", "name": "Allowed Dimension", diff --git a/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json index 5c809515c2..95e98d0b67 100644 --- a/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json +++ b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json @@ -13,7 +13,7 @@ "fieldname": "applicable_on_account", "fieldtype": "Link", "in_list_view": 1, - "label": "Applicable On Account", + "label": "Accounts", "options": "Account", "reqd": 1, "show_days": 1, @@ -33,7 +33,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-11-16 13:36:59.129672", + "modified": "2020-11-22 19:55:13.324136", "modified_by": "Administrator", "module": "Accounts", "name": "Applicable On Account", From 6b9dda5f3eb7af999456ffbf4cc69c255c2f09b2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 Nov 2020 14:49:10 +0530 Subject: [PATCH 128/728] fix: Move filter setup to doctype controllers --- erpnext/accounts/doctype/budget/budget.js | 7 +- .../doctype/journal_entry/journal_entry.js | 5 + .../loyalty_program/loyalty_program.js | 7 + .../opening_invoice_creation_tool.js | 3 + .../doctype/payment_entry/payment_entry.js | 4 + .../doctype/pos_profile/pos_profile.js | 3 + .../purchase_invoice/purchase_invoice.js | 7 + .../doctype/sales_invoice/sales_invoice.js | 8 +- .../doctype/shipping_rule/shipping_rule.js | 10 ++ erpnext/assets/doctype/asset/asset.js | 7 + .../asset_value_adjustment.js | 10 ++ .../doctype/purchase_order/purchase_order.js | 8 +- .../doctype/fee_schedule/fee_schedule.js | 7 + .../doctype/fee_structure/fee_structure.js | 8 + erpnext/education/doctype/fees/fees.js | 7 + .../hr/doctype/expense_claim/expense_claim.js | 20 ++- .../doctype/payroll_entry/payroll_entry.js | 5 + erpnext/public/js/controllers/transaction.js | 4 + .../public/js/utils/dimension_tree_filter.js | 164 ++++++++---------- .../doctype/delivery_note/delivery_note.js | 4 +- .../material_request/material_request.js | 7 + .../purchase_receipt/purchase_receipt.js | 3 + .../stock/doctype/stock_entry/stock_entry.js | 4 + .../stock_reconciliation.js | 7 + 24 files changed, 218 insertions(+), 101 deletions(-) diff --git a/erpnext/accounts/doctype/budget/budget.js b/erpnext/accounts/doctype/budget/budget.js index 1b79398247..e60bc60475 100644 --- a/erpnext/accounts/doctype/budget/budget.js +++ b/erpnext/accounts/doctype/budget/budget.js @@ -1,5 +1,6 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +frappe.provide("erpnext.accounts.dimensions"); frappe.ui.form.on('Budget', { onload: function(frm) { @@ -11,7 +12,7 @@ frappe.ui.form.on('Budget', { is_group: 0 } } - }) + }); frm.set_query("monthly_distribution", function() { return { @@ -19,7 +20,9 @@ frappe.ui.form.on('Budget', { fiscal_year: frm.doc.fiscal_year } } - }) + }); + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, refresh: function(frm) { diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index d60a7b76cc..37b03f3f0e 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -120,6 +120,8 @@ frappe.ui.form.on("Journal Entry", { } } }); + + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, voucher_type: function(frm){ @@ -197,6 +199,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ this.load_defaults(); this.setup_queries(); this.setup_balance_formatter(); + erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype); }, onload_post_render: function() { @@ -397,6 +400,8 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ } } cur_frm.cscript.update_totals(doc); + + erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'accounts'); }, }); diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.js b/erpnext/accounts/doctype/loyalty_program/loyalty_program.js index 0d2b8cbf15..f90f86728d 100644 --- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.js +++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.js @@ -1,6 +1,8 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +frappe.provide("erpnext.accounts.dimensions"); + frappe.ui.form.on('Loyalty Program', { setup: function(frm) { var help_content = @@ -47,11 +49,16 @@ frappe.ui.form.on('Loyalty Program', { }); frm.set_value("company", frappe.defaults.get_user_default("Company")); + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, refresh: function(frm) { if (frm.doc.loyalty_program_type === "Single Tier Program" && frm.doc.collection_rules.length > 1) { frappe.throw(__("Please select the Multiple Tier Program type for more than one collection rules.")); } + }, + + company: function(frm) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); } }); diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js index 3ce5701823..c087980798 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js @@ -36,6 +36,8 @@ frappe.ui.form.on('Opening Invoice Creation Tool', { frm.dashboard.show_progress(data.title, (data.count / data.total) * 100, data.message); frm.page.set_indicator(__('In Progress'), 'orange'); }); + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, refresh: function(frm) { @@ -100,6 +102,7 @@ frappe.ui.form.on('Opening Invoice Creation Tool', { } }) } + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, invoice_type: function(frm) { diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index ea5487d575..4318aea2bd 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -1,6 +1,7 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt {% include "erpnext/public/js/controllers/accounts.js" %} +frappe.provide("erpnext.accounts.dimensions"); frappe.ui.form.on('Payment Entry', { onload: function(frm) { @@ -8,6 +9,8 @@ frappe.ui.form.on('Payment Entry', { if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null); if (!frm.doc.paid_to) frm.set_value("paid_to_account_currency", null); } + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, setup: function(frm) { @@ -158,6 +161,7 @@ frappe.ui.form.on('Payment Entry', { company: function(frm) { frm.events.hide_unhide_fields(frm); frm.events.set_dynamic_labels(frm); + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, contact_person: function(frm) { diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js index 558e21c13a..668cf016d3 100755 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.js +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js @@ -48,6 +48,8 @@ frappe.ui.form.on('POS Profile', { } }; }); + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, refresh: function(frm) { @@ -58,6 +60,7 @@ frappe.ui.form.on('POS Profile', { company: function(frm) { frm.trigger("toggle_display_account_head"); + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, toggle_display_account_head: function(frm) { diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 3c07ee75cb..3fa6ee76e6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -16,6 +16,11 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ }); } }, + + company: function() { + erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype); + }, + onload: function() { this._super(); @@ -31,6 +36,8 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ if (this.frm.doc.supplier && this.frm.doc.__islocal) { this.frm.trigger('supplier'); } + + erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype); }, refresh: function(doc) { diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index e27bd2fa3a..b89cecfab5 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -5,14 +5,17 @@ cur_frm.pformat.print_heading = 'Invoice'; {% include 'erpnext/selling/sales_common.js' %}; - - frappe.provide("erpnext.accounts"); + + erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.extend({ setup: function(doc) { this.setup_posting_date_time_check(); this._super(doc); }, + company: function() { + erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype); + }, onload: function() { var me = this; this._super(); @@ -33,6 +36,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte me.frm.refresh_fields(); } erpnext.queries.setup_warehouse_query(this.frm); + erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype); }, refresh: function(doc, dt, dn) { diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js index 7cfbfed138..8e4b806f02 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js @@ -1,7 +1,17 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt +frappe.provide('erpnext.accounts.dimensions'); + frappe.ui.form.on('Shipping Rule', { + onload: function(frm) { + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); + }, + + company: function(frm) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); + }, + refresh: function(frm) { frm.set_query("account", function() { return { diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 3af3948fac..e9c8aff678 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -2,6 +2,7 @@ // For license information, please see license.txt frappe.provide("erpnext.asset"); +frappe.provide("erpnext.accounts.dimensions"); frappe.ui.form.on('Asset', { onload: function(frm) { @@ -31,6 +32,12 @@ frappe.ui.form.on('Asset', { } }; }); + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); + }, + + company: function(frm) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, setup: function(frm) { diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js index a6e6974c48..79c8861bcd 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js @@ -1,6 +1,8 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +frappe.provide("erpnext.accounts.dimensions"); + frappe.ui.form.on('Asset Value Adjustment', { setup: function(frm) { frm.add_fetch('company', 'cost_center', 'cost_center'); @@ -13,11 +15,19 @@ frappe.ui.form.on('Asset Value Adjustment', { } }); }, + onload: function(frm) { if(frm.is_new() && frm.doc.asset) { frm.trigger("set_current_asset_value"); } + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, + + company: function(frm) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); + }, + asset: function(frm) { frm.trigger("set_current_asset_value"); }, diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 2f52a9e035..92e2708eab 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -2,7 +2,7 @@ // License: GNU General Public License v3. See license.txt frappe.provide("erpnext.buying"); - +frappe.provide("erpnext.accounts.dimensions"); {% include 'erpnext/public/js/controllers/buying.js' %}; frappe.ui.form.on("Purchase Order", { @@ -30,6 +30,10 @@ frappe.ui.form.on("Purchase Order", { }, + company: function(frm) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); + }, + onload: function(frm) { set_schedule_date(frm); if (!frm.doc.transaction_date){ @@ -39,6 +43,8 @@ frappe.ui.form.on("Purchase Order", { erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); }); + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); } }); diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.js b/erpnext/education/doctype/fee_schedule/fee_schedule.js index 75dd4469e8..65b5fa6cf2 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.js +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.js @@ -1,6 +1,7 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +frappe.provide("erpnext.accounts.dimensions"); frappe.ui.form.on('Fee Schedule', { setup: function(frm) { frm.add_fetch('fee_structure', 'receivable_account', 'receivable_account'); @@ -8,6 +9,10 @@ frappe.ui.form.on('Fee Schedule', { frm.add_fetch('fee_structure', 'cost_center', 'cost_center'); }, + company: function(frm) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); + }, + onload: function(frm) { frm.set_query('receivable_account', function(doc) { return { @@ -50,6 +55,8 @@ frappe.ui.form.on('Fee Schedule', { } } }); + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, refresh: function(frm) { diff --git a/erpnext/education/doctype/fee_structure/fee_structure.js b/erpnext/education/doctype/fee_structure/fee_structure.js index b331c6d3c0..310c4105f4 100644 --- a/erpnext/education/doctype/fee_structure/fee_structure.js +++ b/erpnext/education/doctype/fee_structure/fee_structure.js @@ -1,6 +1,8 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +frappe.provide("erpnext.accounts.dimensions"); + frappe.ui.form.on('Fee Structure', { setup: function(frm) { frm.add_fetch('company', 'default_receivable_account', 'receivable_account'); @@ -8,6 +10,10 @@ frappe.ui.form.on('Fee Structure', { frm.add_fetch('company', 'cost_center', 'cost_center'); }, + company: function(frm) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); + }, + onload: function(frm) { frm.set_query('academic_term', function() { return { @@ -35,6 +41,8 @@ frappe.ui.form.on('Fee Structure', { } }; }); + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, refresh: function(frm) { diff --git a/erpnext/education/doctype/fees/fees.js b/erpnext/education/doctype/fees/fees.js index aaf42b4751..433bd64d2f 100644 --- a/erpnext/education/doctype/fees/fees.js +++ b/erpnext/education/doctype/fees/fees.js @@ -1,6 +1,7 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +frappe.provide("erpnext.accounts.dimensions"); frappe.ui.form.on("Fees", { setup: function(frm) { @@ -9,6 +10,10 @@ frappe.ui.form.on("Fees", { frm.add_fetch("fee_structure", "cost_center", "cost_center"); }, + company: function(frm) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); + }, + onload: function(frm){ frm.set_query("academic_term",function(){ return{ @@ -45,6 +50,8 @@ frappe.ui.form.on("Fees", { if (!frm.doc.posting_date) { frm.doc.posting_date = frappe.datetime.get_today(); } + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, refresh: function(frm) { diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index cbafd7d3ac..e399b22f90 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -2,11 +2,21 @@ // License: GNU General Public License v3. See license.txt frappe.provide("erpnext.hr"); +frappe.provide("erpnext.accounts.dimensions"); -erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({ - expense_type: function(doc, cdt, cdn) { +frappe.ui.form.on('Expense Claim', { + onload: function(frm) { + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); + }, + company: function(frm) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); + }, +}); + +frappe.ui.form.on('Expense Claim Detail', { + expense_type: function(frm, cdt, cdn) { var d = locals[cdt][cdn]; - if(!doc.company) { + if(!frm.doc.company) { d.expense_type = ""; frappe.msgprint(__("Please set the Company")); this.frm.refresh_fields(); @@ -20,7 +30,7 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({ method: "erpnext.hr.doctype.expense_claim.expense_claim.get_expense_claim_account_and_cost_center", args: { "expense_claim_type": d.expense_type, - "company": doc.company + "company": frm.doc.company }, callback: function(r) { if (r.message) { @@ -32,8 +42,6 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({ } }); -$.extend(cur_frm.cscript, new erpnext.hr.ExpenseClaimController({frm: cur_frm})); - cur_frm.add_fetch('employee', 'company', 'company'); cur_frm.add_fetch('employee','employee_name','employee_name'); cur_frm.add_fetch('expense_type','description','description'); diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js index d32fdbcaf1..410840771c 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js @@ -3,6 +3,8 @@ var in_progress = false; +frappe.provide("erpnext.accounts.dimensions"); + frappe.ui.form.on('Payroll Entry', { onload: function (frm) { if (!frm.doc.posting_date) { @@ -17,6 +19,8 @@ frappe.ui.form.on('Payroll Entry', { } }; }); + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, refresh: function(frm) { @@ -122,6 +126,7 @@ frappe.ui.form.on('Payroll Entry', { company: function (frm) { frm.events.clear_employee_table(frm); + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, department: function (frm) { diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index ec6b3dc6df..3f293782b4 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1,6 +1,8 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt +frappe.provide('erpnext.accounts.dimensions'); + erpnext.TransactionController = erpnext.taxes_and_totals.extend({ setup: function() { this._super(); @@ -106,6 +108,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if(!item.warehouse && frm.doc.set_warehouse) { item.warehouse = frm.doc.set_warehouse; } + + erpnext.accounts.dimensions.copy_dimension_from_first_row(frm, cdt, cdn, 'items'); } }); diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index 7a42fb5614..c79736d0e1 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -1,60 +1,79 @@ -frappe.provide('frappe.ui.form'); -let default_dimensions = {}; +frappe.provide('erpnext.accounts'); -let doctypes_with_dimensions = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset", - "Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Shipping Rule", "Loyalty Program", - "Fee Schedule", "Fee Structure", "Stock Reconciliation", "Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", - "Subscription", "Purchase Order", "Journal Entry", "Material Request", "Purchase Receipt", "Asset", "Asset Value Adjustment"]; - -let child_docs = ["Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", - "Material Request Item", "Delivery Note Item", "Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", - "Landed Cost Item", "Asset Value Adjustment", "Opening Invoice Creation Tool Item", "Subscription Plan", - "Sales Taxes and Charges", "Purchase Taxes and Charges"]; - -frappe.call({ - method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimension_filters", - args: { - 'with_costcenter_and_project': true +erpnext.accounts.dimensions = { + setup_dimension_filters(frm, doctype) { + this.accounting_dimensions = []; + this.default_dimensions = {}; + this.fetch_custom_dimensions(frm, doctype); }, - callback: function(r) { - erpnext.dimension_filters = r.message[0]; - default_dimensions = r.message[1]; - } -}); -doctypes_with_dimensions.forEach((doctype) => { - frappe.ui.form.on(doctype, { - onload: function(frm) { - erpnext.dimension_filters.forEach((dimension) => { - frappe.model.with_doctype(dimension['document_type'], () => { - let parent_fields = []; - frappe.meta.get_docfields(doctype).forEach((df) => { - if (df.fieldtype === 'Link' && df.options === 'Account') { - parent_fields.push(df.fieldname); - } else if (df.fieldtype === 'Table') { - setup_child_filters(frm, df.options, df.fieldname, dimension['fieldname']); - } + fetch_custom_dimensions(frm, doctype) { + let me = this; + frappe.call({ + method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimensions", + args: { + 'with_cost_center_and_project': true + }, + callback: function(r) { + me.accounting_dimensions = r.message[0]; + me.default_dimensions = r.message[1]; + me.setup_filters(frm, doctype); + } + }); + }, - setup_account_filters(frm, dimension['fieldname'], parent_fields); - }); + setup_filters(frm, doctype) { + this.accounting_dimensions.forEach((dimension) => { + frappe.model.with_doctype(dimension['document_type'], () => { + let parent_fields = []; + frappe.meta.get_docfields(doctype).forEach((df) => { + if (df.fieldtype === 'Link' && df.options === 'Account') { + parent_fields.push(df.fieldname); + } else if (df.fieldtype === 'Table') { + this.setup_child_filters(frm, df.options, df.fieldname, dimension['fieldname']); + } + + if (frappe.meta.has_field(doctype, dimension['fieldname'])) { + this.setup_account_filters(frm, dimension['fieldname'], parent_fields); + } }); }); - }, + }); + }, - company: function(frm) { - if(frm.doc.company && (Object.keys(default_dimensions || {}).length > 0) - && default_dimensions[frm.doc.company]) { - frm.trigger('update_dimension'); - } - }, + setup_child_filters(frm, doctype, parentfield, dimension) { + let fields = []; - update_dimension: function(frm) { - erpnext.dimension_filters.forEach((dimension) => { + if (frappe.meta.has_field(doctype, dimension)) { + frappe.model.with_doctype(doctype, () => { + frappe.meta.get_docfields(doctype).forEach((df) => { + if (df.fieldtype === 'Link' && df.options === 'Account') { + fields.push(df.fieldname); + } + }); + + frm.set_query(dimension, parentfield, function(doc, cdt, cdn) { + let row = locals[cdt][cdn]; + return erpnext.queries.get_filtered_dimensions(row, fields, dimension, doc.company); + }); + }); + } + }, + + setup_account_filters(frm, dimension, fields) { + frm.set_query(dimension, function(doc) { + return erpnext.queries.get_filtered_dimensions(doc, fields, dimension, doc.company); + }); + }, + + update_dimension(frm, doctype) { + if (this.accounting_dimensions) { + this.accounting_dimensions.forEach((dimension) => { if(frm.is_new()) { - if(frm.doc.company && Object.keys(default_dimensions || {}).length > 0 - && default_dimensions[frm.doc.company]) { + if(frm.doc.company && Object.keys(this.default_dimensions || {}).length > 0 + && this.default_dimensions[frm.doc.company]) { - let default_dimension = default_dimensions[frm.doc.company][dimension['fieldname']]; + let default_dimension = this.default_dimensions[frm.doc.company][dimension['fieldname']]; if(default_dimension) { if (frappe.meta.has_field(doctype, dimension['fieldname'])) { @@ -69,47 +88,14 @@ doctypes_with_dimensions.forEach((doctype) => { } }); } - }); -}); + }, -child_docs.forEach((doctype) => { - frappe.ui.form.on(doctype, { - items_add: function(frm, cdt, cdn) { - copy_dimension(frm, cdt, cdn, "items"); - }, - - accounts_add: function(frm, cdt, cdn) { - copy_dimension(frm, cdt, cdn, "accounts"); + copy_dimension_from_first_row(frm, cdt, cdn, fieldname) { + if (frappe.meta.has_field(frm.doctype, fieldname)) { + this.accounting_dimensions.forEach((dimension) => { + let row = frappe.get_doc(cdt, cdn); + frm.script_manager.copy_from_first_row(fieldname, row, [dimension['fieldname']]); + }); } - }); -}); - -let copy_dimension = function(frm, cdt, cdn, fieldname) { - erpnext.dimension_filters.forEach((dimension) => { - let row = frappe.get_doc(cdt, cdn); - frm.script_manager.copy_from_first_row(fieldname, row, [dimension['fieldname']]); - }); -}; - -let setup_child_filters = function(frm, doctype, parentfield, dimension) { - let fields = []; - - frappe.model.with_doctype(doctype, () => { - frappe.meta.get_docfields(doctype).forEach((df) => { - if (df.fieldtype === 'Link' && df.options === 'Account') { - fields.push(df.fieldname); - } - }); - - frm.set_query(dimension, parentfield, function(doc, cdt, cdn) { - let row = locals[cdt][cdn]; - return erpnext.queries.get_filtered_dimensions(row, fields, dimension, doc.company); - }); - }); -}; - -let setup_account_filters = function(frm, dimension, fields) { - frm.set_query(dimension, function(doc) { - return erpnext.queries.get_filtered_dimensions(doc, fields, dimension, doc.company); - }); -}; \ No newline at end of file + } +} \ No newline at end of file diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 251a26a592..ccc719c26d 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -7,6 +7,7 @@ cur_frm.add_fetch('customer', 'tax_id', 'tax_id'); frappe.provide("erpnext.stock"); frappe.provide("erpnext.stock.delivery_note"); +frappe.provide("erpnext.accounts.dimensions"); frappe.ui.form.on("Delivery Note", { setup: function(frm) { @@ -75,7 +76,7 @@ frappe.ui.form.on("Delivery Note", { } }); - + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, print_without_amount: function(frm) { @@ -305,6 +306,7 @@ frappe.ui.form.on('Delivery Note', { company: function(frm) { frm.trigger("unhide_account_head"); + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, unhide_account_head: function(frm) { diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 01edd99e9d..527b0d3ea9 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -2,6 +2,7 @@ // License: GNU General Public License v3. See license.txt // eslint-disable-next-line +frappe.provide("erpnext.accounts.dimensions"); {% include 'erpnext/public/js/controllers/buying.js' %}; frappe.ui.form.on('Material Request', { @@ -66,6 +67,12 @@ frappe.ui.form.on('Material Request', { filters: {'company': doc.company} }; }); + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); + }, + + company: function(frm) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, onload_post_render: function(frm) { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index bc1d81d356..d998729c47 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -46,6 +46,8 @@ frappe.ui.form.on("Purchase Receipt", { erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); }); + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, refresh: function(frm) { @@ -75,6 +77,7 @@ frappe.ui.form.on("Purchase Receipt", { company: function(frm) { frm.trigger("toggle_display_account_head"); + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, toggle_display_account_head: function(frm) { diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 91217582ca..80f18a63f5 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -1,6 +1,7 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt frappe.provide("erpnext.stock"); +frappe.provide("erpnext.accounts.dimensions"); frappe.ui.form.on('Stock Entry', { setup: function(frm) { @@ -97,6 +98,7 @@ frappe.ui.form.on('Stock Entry', { }); frm.add_fetch("bom_no", "inspection_required", "inspection_required"); + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, setup_quality_inspection: function(frm) { @@ -312,6 +314,8 @@ frappe.ui.form.on('Stock Entry', { frm.set_value("letter_head", company_doc.default_letter_head); } frm.trigger("toggle_display_account_head"); + + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); } }, diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js index e2121fce3f..be9404d9c8 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js @@ -2,6 +2,7 @@ // License: GNU General Public License v3. See license.txt frappe.provide("erpnext.stock"); +frappe.provide("erpnext.accounts.dimensions") frappe.ui.form.on("Stock Reconciliation", { onload: function(frm) { @@ -26,6 +27,12 @@ frappe.ui.form.on("Stock Reconciliation", { if (!frm.doc.expense_account) { frm.trigger("set_expense_account"); } + + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); + }, + + company: function(frm) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, refresh: function(frm) { From 1087d97c03a0ea9973ffc0d70472c4a3fcac8654 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 26 Nov 2020 10:45:44 +0530 Subject: [PATCH 129/728] feat: Warehouse Capacity Summary - Added Page Warehouse Capacity Summary - Added Page to Desk and Putaway List View - Reused Item Dashboard/Stock Balance page render code - Added naming series to Putaway Rule --- erpnext/public/build.json | 4 +- erpnext/stock/dashboard/item_dashboard.js | 75 ++++++++--- .../dashboard/warehouse_capacity_dashboard.py | 69 ++++++++++ erpnext/stock/desk_page/stock/stock.json | 4 +- erpnext/stock/doctype/item/item.js | 5 +- .../purchase_receipt/purchase_receipt.json | 9 +- .../doctype/putaway_rule/putaway_rule.json | 8 +- .../doctype/putaway_rule/putaway_rule_list.js | 10 +- .../stock/page/stock_balance/stock_balance.js | 3 + .../warehouse_capacity_summary/__init__.py | 0 .../warehouse_capacity_summary.html | 40 ++++++ .../warehouse_capacity_summary.js | 120 ++++++++++++++++++ .../warehouse_capacity_summary.json | 26 ++++ .../warehouse_capacity_summary_header.html | 19 +++ 14 files changed, 367 insertions(+), 25 deletions(-) create mode 100644 erpnext/stock/dashboard/warehouse_capacity_dashboard.py create mode 100644 erpnext/stock/page/warehouse_capacity_summary/__init__.py create mode 100644 erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html create mode 100644 erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js create mode 100644 erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.json create mode 100644 erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 2695502269..8b18a1fcfb 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -54,6 +54,8 @@ "js/item-dashboard.min.js": [ "stock/dashboard/item_dashboard.html", "stock/dashboard/item_dashboard_list.html", - "stock/dashboard/item_dashboard.js" + "stock/dashboard/item_dashboard.js", + "stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html", + "stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html" ] } diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index 9bd03d45cb..abc286fcc6 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -24,6 +24,16 @@ erpnext.stock.ItemDashboard = Class.extend({ handle_move_add($(this), "Add") }); + this.content.on('click', '.btn-edit', function() { + let item = unescape($(this).attr('data-item')); + let warehouse = unescape($(this).attr('data-warehouse')); + let company = unescape($(this).attr('data-company')); + frappe.db.get_value('Putaway Rule', + {'item_code': item, 'warehouse': warehouse, 'company': company}, 'name', (r) => { + frappe.set_route("Form", "Putaway Rule", r.name); + }); + }); + function handle_move_add(element, action) { let item = unescape(element.attr('data-item')); let warehouse = unescape(element.attr('data-warehouse')); @@ -59,7 +69,7 @@ erpnext.stock.ItemDashboard = Class.extend({ // more this.content.find('.btn-more').on('click', function() { - me.start += 20; + me.start += this.page_length; me.refresh(); }); @@ -69,33 +79,41 @@ erpnext.stock.ItemDashboard = Class.extend({ this.before_refresh(); } + let args = { + item_code: this.item_code, + warehouse: this.warehouse, + parent_warehouse: this.parent_warehouse, + item_group: this.item_group, + company: this.company, + start: this.start, + sort_by: this.sort_by, + sort_order: this.sort_order + } + var me = this; frappe.call({ - method: 'erpnext.stock.dashboard.item_dashboard.get_data', - args: { - item_code: this.item_code, - warehouse: this.warehouse, - item_group: this.item_group, - start: this.start, - sort_by: this.sort_by, - sort_order: this.sort_order, - }, + method: this.method, + args: args, callback: function(r) { me.render(r.message); } }); }, render: function(data) { - if(this.start===0) { + if (this.start===0) { this.max_count = 0; this.result.empty(); } + if (this.page_name === "warehouse-capacity-summary") { + var context = this.get_capacity_dashboard_data(data); + } else { + var context = this.get_item_dashboard_data(data, this.max_count, true); + } - var context = this.get_item_dashboard_data(data, this.max_count, true); this.max_count = this.max_count; // show more button - if(data && data.length===21) { + if (data && data.length===(this.page_length + 1)) { this.content.find('.more').removeClass('hidden'); // remove the last element @@ -106,12 +124,17 @@ erpnext.stock.ItemDashboard = Class.extend({ // If not any stock in any warehouses provide a message to end user if (context.data.length > 0) { - $(frappe.render_template('item_dashboard_list', context)).appendTo(this.result); + this.content.find('.result').css('text-align', 'unset'); + $(frappe.render_template(this.template, context)).appendTo(this.result); } else { - var message = __("Currently no stock available in any warehouse"); - $(` ${message} `).appendTo(this.result); + var message = __("No Stock Available Currently"); + this.content.find('.result').css('text-align', 'center'); + + $(`
+ ${message}
`).appendTo(this.result); } }, + get_item_dashboard_data: function(data, max_count, show_item) { if(!max_count) max_count = 0; if(!data) data = []; @@ -128,7 +151,7 @@ erpnext.stock.ItemDashboard = Class.extend({ d.total_reserved, max_count); }); - var can_write = 0; + let can_write = 0; if(frappe.boot.user.can_write.indexOf("Stock Entry")>=0){ can_write = 1; } @@ -139,6 +162,24 @@ erpnext.stock.ItemDashboard = Class.extend({ can_write:can_write, show_item: show_item || false } + }, + + get_capacity_dashboard_data: function(data) { + if(!data) data = []; + + data.forEach(function(d) { + d.color = d.percent_occupied >=80 ? "#f8814f" : "#2490ef"; + }); + + let can_write = 0; + if(frappe.boot.user.can_write.indexOf("Putaway Rule")>=0){ + can_write = 1; + } + + return { + data: data, + can_write: can_write, + } } }) diff --git a/erpnext/stock/dashboard/warehouse_capacity_dashboard.py b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py new file mode 100644 index 0000000000..ab573e566a --- /dev/null +++ b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py @@ -0,0 +1,69 @@ +from __future__ import unicode_literals + +import frappe +from frappe.model.db_query import DatabaseQuery +from frappe.utils import nowdate +from frappe.utils import flt +from erpnext.stock.utils import get_stock_balance + +@frappe.whitelist() +def get_data(item_code=None, warehouse=None, parent_warehouse=None, + company=None, start=0, sort_by="stock_capacity", sort_order="desc"): + """Return data to render the warehouse capacity dashboard.""" + filters = get_filters(item_code, warehouse, parent_warehouse, company) + + no_permission, filters = get_warehouse_filter_based_on_permissions(filters) + if no_permission: + return [] + + capacity_data = get_warehouse_capacity_data(filters, start) + + asc_desc = -1 if sort_order == "desc" else 1 + capacity_data = sorted(capacity_data, key = lambda i: (i[sort_by] * asc_desc)) + + return capacity_data + +def get_filters(item_code=None, warehouse=None, parent_warehouse=None, + company=None): + filters = [['disable', '=', 0]] + if item_code: + filters.append(['item_code', '=', item_code]) + if warehouse: + filters.append(['warehouse', '=', warehouse]) + if company: + filters.append(['company', '=', company]) + if parent_warehouse: + lft, rgt = frappe.db.get_value("Warehouse", parent_warehouse, ["lft", "rgt"]) + warehouses = frappe.db.sql_list(""" + select name from `tabWarehouse` + where lft >=%s and rgt<=%s + """, (lft, rgt)) + filters.append(['warehouse', 'in', warehouses]) + return filters + +def get_warehouse_filter_based_on_permissions(filters): + try: + # check if user has any restrictions based on user permissions on warehouse + if DatabaseQuery('Warehouse', user=frappe.session.user).build_match_conditions(): + filters.append(['warehouse', 'in', [w.name for w in frappe.get_list('Warehouse')]]) + return False, filters + except frappe.PermissionError: + # user does not have access on warehouse + return True, [] + +def get_warehouse_capacity_data(filters, start): + capacity_data = frappe.db.get_all('Putaway Rule', + fields=['item_code', 'warehouse','stock_capacity', 'company'], + filters=filters, + limit_start=start, + limit_page_length='11' + ) + + for entry in capacity_data: + balance_qty = get_stock_balance(entry.item_code, entry.warehouse, nowdate()) or 0 + entry.update({ + 'actual_qty': balance_qty, + 'percent_occupied': flt((flt(balance_qty) / flt(entry.stock_capacity)) * 100, 0) + }) + + return capacity_data \ No newline at end of file diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index aa4fc28ec9..0038c0a971 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -13,7 +13,7 @@ { "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]" + "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 {\n \"dependencies\": [\n \"Item\",\n \"Putaway Rule\"\n ],\n \"label\": \"Warehouse Capacity Summary\",\n \"name\": \"warehouse-capacity-summary\",\n \"type\": \"page\"\n }\n]" }, { "hidden": 0, @@ -58,7 +58,7 @@ "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-11-24 15:43:20.496057", + "modified": "2020-11-26 10:43:48.286663", "modified_by": "Administrator", "module": "Stock", "name": "Stock", diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index faeeb578fe..ec32b0f044 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -384,7 +384,10 @@ $.extend(erpnext.item, { ' + __("Stock Levels") + ''); erpnext.item.item_dashboard = new erpnext.stock.ItemDashboard({ parent: section, - item_code: frm.doc.name + item_code: frm.doc.name, + page_length: 20, + method: 'erpnext.stock.dashboard.item_dashboard.get_data', + template: 'item_dashboard_list' }); erpnext.item.item_dashboard.refresh(); }); diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 13c8ceb759..7213eb8616 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -21,6 +21,7 @@ "posting_date", "posting_time", "set_posting_time", + "apply_putaway_rule", "is_return", "return_against", "section_addresses", @@ -1104,13 +1105,19 @@ "fieldtype": "Small Text", "label": "Billing Address", "read_only": 1 + }, + { + "default": "0", + "fieldname": "apply_putaway_rule", + "fieldtype": "Check", + "label": "Apply Putaway Rule" } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-10-30 14:00:08.347534", + "modified": "2020-11-25 18:31:32.234503", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.json b/erpnext/stock/doctype/putaway_rule/putaway_rule.json index 325e6f1355..a003f4986f 100644 --- a/erpnext/stock/doctype/putaway_rule/putaway_rule.json +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.json @@ -1,6 +1,6 @@ { "actions": [], - "autoname": "format:{item_code}-{warehouse}", + "autoname": "PUT-.####", "creation": "2020-11-09 11:39:46.489501", "doctype": "DocType", "editable_grid": 1, @@ -90,12 +90,14 @@ "fieldname": "uom", "fieldtype": "Link", "label": "UOM", + "no_copy": 1, "options": "UOM" }, { "fieldname": "stock_capacity", "fieldtype": "Float", "label": "Capacity in Stock UOM", + "no_copy": 1, "read_only": 1 }, { @@ -103,12 +105,13 @@ "fieldname": "conversion_factor", "fieldtype": "Float", "label": "Conversion Factor", + "no_copy": 1, "read_only": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2020-11-24 16:20:18.306671", + "modified": "2020-11-25 20:39:19.973437", "modified_by": "Administrator", "module": "Stock", "name": "Putaway Rule", @@ -152,5 +155,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "title_field": "item_code", "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js b/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js index e48c415f14..725e91ee8d 100644 --- a/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js @@ -6,5 +6,13 @@ frappe.listview_settings['Putaway Rule'] = { } else { return [__("Active"), "blue", "disable,=,0"]; } - } + }, + + reports: [ + { + name: 'Warehouse Capacity Summary', + report_type: 'Page', + route: 'warehouse-capacity-summary' + } + ] }; diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js index da21c6bc64..bddffd465e 100644 --- a/erpnext/stock/page/stock_balance/stock_balance.js +++ b/erpnext/stock/page/stock_balance/stock_balance.js @@ -65,6 +65,9 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) { frappe.require('assets/js/item-dashboard.min.js', function() { page.item_dashboard = new erpnext.stock.ItemDashboard({ parent: page.main, + page_length: 20, + method: 'erpnext.stock.dashboard.item_dashboard.get_data', + template: 'item_dashboard_list' }) page.item_dashboard.before_refresh = function() { diff --git a/erpnext/stock/page/warehouse_capacity_summary/__init__.py b/erpnext/stock/page/warehouse_capacity_summary/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html new file mode 100644 index 0000000000..90112c78a8 --- /dev/null +++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html @@ -0,0 +1,40 @@ +{% for d in data %} +
+
+ + +
+ {{ d.stock_capacity }} +
+
+ {{ d.actual_qty }} +
+
+
+
+
+
+
+
+ {{ d.percent_occupied }}% +
+ {% if can_write %} +
+
+ {% endif %} +
+
+{% endfor %} \ No newline at end of file diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js new file mode 100644 index 0000000000..c3b3b5d8ec --- /dev/null +++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js @@ -0,0 +1,120 @@ +frappe.pages['warehouse-capacity-summary'].on_page_load = function(wrapper) { + var page = frappe.ui.make_app_page({ + parent: wrapper, + title: 'Warehouse Capacity Summary', + single_column: true + }); + page.set_secondary_action('Refresh', () => page.capacity_dashboard.refresh(), 'octicon octicon-sync'); + page.start = 0; + + page.company_field = page.add_field({ + fieldname: 'company', + label: __('Company'), + fieldtype:'Link', + options:'Company', + reqd: 1, + default: frappe.defaults.get_default("company"), + change: function() { + page.capacity_dashboard.start = 0; + page.capacity_dashboard.refresh(); + } + }); + + page.warehouse_field = page.add_field({ + fieldname: 'warehouse', + label: __('Warehouse'), + fieldtype:'Link', + options:'Warehouse', + change: function() { + page.capacity_dashboard.start = 0; + page.capacity_dashboard.refresh(); + } + }); + + page.item_field = page.add_field({ + fieldname: 'item_code', + label: __('Item'), + fieldtype:'Link', + options:'Item', + change: function() { + page.capacity_dashboard.start = 0; + page.capacity_dashboard.refresh(); + } + }); + + page.parent_warehouse_field = page.add_field({ + fieldname: 'parent_warehouse', + label: __('Parent Warehouse'), + fieldtype:'Link', + options:'Warehouse', + get_query: function() { + return { + filters: { + "is_group": 1 + } + }; + }, + change: function() { + page.capacity_dashboard.start = 0; + page.capacity_dashboard.refresh(); + } + }); + + page.sort_selector = new frappe.ui.SortSelector({ + parent: page.wrapper.find('.page-form'), + args: { + sort_by: 'stock_capacity', + sort_order: 'desc', + options: [ + {fieldname: 'stock_capacity', label: __('Capacity (Stock UOM)')}, + {fieldname: 'percent_occupied', label:__('% Occupied')}, + {fieldname: 'actual_qty', label:__('Balance Qty (Stock ')} + ] + }, + change: function(sort_by, sort_order) { + page.capacity_dashboard.sort_by = sort_by; + page.capacity_dashboard.sort_order = sort_order; + page.capacity_dashboard.start = 0; + page.capacity_dashboard.refresh(); + } + }); + + frappe.require('assets/js/item-dashboard.min.js', function() { + $(frappe.render_template('warehouse_capacity_summary_header')).appendTo(page.main); + + page.capacity_dashboard = new erpnext.stock.ItemDashboard({ + page_name: "warehouse-capacity-summary", + page_length: 10, + parent: page.main, + sort_by: 'stock_capacity', + sort_order: 'desc', + method: 'erpnext.stock.dashboard.warehouse_capacity_dashboard.get_data', + template: 'warehouse_capacity_summary' + }) + + page.capacity_dashboard.before_refresh = function() { + this.item_code = page.item_field.get_value(); + this.warehouse = page.warehouse_field.get_value(); + this.parent_warehouse = page.parent_warehouse_field.get_value(); + this.company = page.company_field.get_value(); + } + + page.capacity_dashboard.refresh(); + + let setup_click = function(doctype) { + page.main.on('click', 'a[data-type="'+ doctype.toLowerCase() +'"]', function() { + var name = $(this).attr('data-name'); + var field = page[doctype.toLowerCase() + '_field']; + if(field.get_value()===name) { + frappe.set_route('Form', doctype, name) + } else { + field.set_input(name); + page.capacity_dashboard.refresh(); + } + }); + } + + setup_click('Item'); + setup_click('Warehouse'); + }); +} \ No newline at end of file diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.json b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.json new file mode 100644 index 0000000000..a6e5b45332 --- /dev/null +++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.json @@ -0,0 +1,26 @@ +{ + "content": null, + "creation": "2020-11-25 12:07:54.056208", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2020-11-25 11:07:54.056208", + "modified_by": "Administrator", + "module": "Stock", + "name": "warehouse-capacity-summary", + "owner": "Administrator", + "page_name": "Warehouse Capacity Summary", + "roles": [ + { + "role": "Stock User" + }, + { + "role": "Stock Manager" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0, + "title": "Warehouse Capacity Summary" +} \ No newline at end of file diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html new file mode 100644 index 0000000000..acaf180a90 --- /dev/null +++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html @@ -0,0 +1,19 @@ +
+
+
+ Warehouse +
+
+ Item +
+
+ Stock Capacity +
+
+ Balance Stock Qty +
+
+ % Occupied +
+
+
\ No newline at end of file From 54228691e7e64ac181cb308520708e9f97a2694c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 26 Nov 2020 16:38:28 +0530 Subject: [PATCH 130/728] 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 131/728] 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 132/728] 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 133/728] 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 2c114053ad3087ad12a8d8a084cabb817b126066 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 27 Nov 2020 15:05:28 +0530 Subject: [PATCH 134/728] feat: Patient History Settings --- .../__init__.py | 0 .../patient_history_custom_document_type.json | 46 ++++++++++ .../patient_history_custom_document_type.py | 10 +++ .../patient_history_settings/__init__.py | 0 .../patient_history_settings.js | 85 +++++++++++++++++++ .../patient_history_settings.json | 55 ++++++++++++ .../patient_history_settings.py | 10 +++ .../test_patient_history_settings.py | 10 +++ .../__init__.py | 0 ...atient_history_standard_document_type.json | 47 ++++++++++ .../patient_history_standard_document_type.py | 10 +++ 11 files changed, 273 insertions(+) create mode 100644 erpnext/healthcare/doctype/patient_history_custom_document_type/__init__.py create mode 100644 erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json create mode 100644 erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.py create mode 100644 erpnext/healthcare/doctype/patient_history_settings/__init__.py create mode 100644 erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js create mode 100644 erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.json create mode 100644 erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py create mode 100644 erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py create mode 100644 erpnext/healthcare/doctype/patient_history_standard_document_type/__init__.py create mode 100644 erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json create mode 100644 erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.py diff --git a/erpnext/healthcare/doctype/patient_history_custom_document_type/__init__.py b/erpnext/healthcare/doctype/patient_history_custom_document_type/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json new file mode 100644 index 0000000000..a158075e7b --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json @@ -0,0 +1,46 @@ +{ + "actions": [], + "creation": "2020-11-25 13:40:23.054469", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "select_fields", + "selected_fields" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "select_fields", + "fieldtype": "Button", + "in_list_view": 1, + "label": "Select Fields" + }, + { + "fieldname": "selected_fields", + "fieldtype": "Code", + "label": "selected_fields" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-11-25 14:19:33.637543", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient History Custom Document Type", + "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/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.py b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.py new file mode 100644 index 0000000000..f0a1f929f4 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.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 PatientHistoryCustomDocumentType(Document): + pass diff --git a/erpnext/healthcare/doctype/patient_history_settings/__init__.py b/erpnext/healthcare/doctype/patient_history_settings/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js new file mode 100644 index 0000000000..155476e2b1 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js @@ -0,0 +1,85 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Patient History Settings', { + refresh: function(frm) { + frm.set_query('document_type', 'custom_doctypes', () => { + return { + filters: { + custom: 1, + module: 'Healthcare' + } + }; + }); + }, + + field_selector: function(frm, doc) { + let document_fields = (JSON.parse(doc.selected_fields)).map(f => f.fieldname); + let d = new frappe.ui.Dialog({ + title: __('{0} Fields', [__(doc.document_type)]), + fields: [ + { + label: __('Select Fields'), + fieldtype: 'MultiCheck', + fieldname: 'fields', + options: frm.events.get_doctype_fields(frm, doc.document_type, document_fields), + columns: 2 + } + ] + }); + + d.set_primary_action(__('Save'), () => { + let values = d.get_values().fields; + + let selected_fields = []; + + for (let idx in values) { + let value = values[idx]; + + let field = frappe.meta.get_docfield(doc.document_type, value); + if (field) { + selected_fields.push({ + label: field.label, + fieldname: field.fieldname + }); + } + } + + frappe.model.set_value('Patient History Custom Document Type', doc.name, 'selected_fields', JSON.stringify(selected_fields)); + d.hide(); + }); + + d.show(); + }, + + get_doctype_fields(frm, document_type, fields) { + let multiselect_fields = []; + + frappe.model.with_doctype(document_type, () => { + // get doctype fields + frappe.get_doc('DocType', document_type).fields.forEach(field => { + if (!in_list(frappe.model.no_value_type, field.fieldtype) && !field.hidden) { + multiselect_fields.push({ + label: field.label, + value: field.fieldname, + checked: in_list(fields, field.fieldname) + }); + } + }); + }); + + return multiselect_fields; + } +}); + +frappe.ui.form.on('Patient History Custom Document Type', { + select_fields: function(frm) { + let doc = frm.selected_doc; + + if (!doc.document_type) + frappe.throw(__('Select the Document Type first.')) + + frm.events.field_selector(frm, doc); + } + +}); diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.json b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.json new file mode 100644 index 0000000000..143e2c91eb --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.json @@ -0,0 +1,55 @@ +{ + "actions": [], + "creation": "2020-11-25 13:41:37.675518", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "standard_doctypes", + "section_break_2", + "custom_doctypes" + ], + "fields": [ + { + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "fieldname": "custom_doctypes", + "fieldtype": "Table", + "label": "Custom Document Types", + "options": "Patient History Custom Document Type" + }, + { + "fieldname": "standard_doctypes", + "fieldtype": "Table", + "label": "Standard Document Types", + "options": "Patient History Standard Document Type", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2020-11-25 13:43:38.511771", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient History Settings", + "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/healthcare/doctype/patient_history_settings/patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py new file mode 100644 index 0000000000..27cbf2fc60 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_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 PatientHistorySettings(Document): + pass diff --git a/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py new file mode 100644 index 0000000000..548c423670 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_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 TestPatientHistorySettings(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/patient_history_standard_document_type/__init__.py b/erpnext/healthcare/doctype/patient_history_standard_document_type/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json new file mode 100644 index 0000000000..ec40d893eb --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json @@ -0,0 +1,47 @@ +{ + "actions": [], + "creation": "2020-11-25 13:39:36.014814", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "select_fields", + "selected_fields" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "select_fields", + "fieldtype": "Button", + "in_list_view": 1, + "label": "Select Fields" + }, + { + "fieldname": "selected_fields", + "fieldtype": "Code", + "label": "Selected Fields" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-11-25 14:19:53.708991", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient History Standard Document Type", + "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/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.py b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.py new file mode 100644 index 0000000000..2d94911855 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.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 PatientHistoryStandardDocumentType(Document): + pass From f2932d720882433e18c690be130a7d919a0570d6 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sat, 28 Nov 2020 19:19:58 +0530 Subject: [PATCH 135/728] feat: set date field in Settings for Patient Medical Record --- .../patient_history_custom_document_type.json | 24 ++++++++++++------- .../patient_history_settings.js | 13 ++++------ .../patient_history_settings.py | 17 +++++++++++-- ...atient_history_standard_document_type.json | 16 ++++++------- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json index a158075e7b..7986e48ced 100644 --- a/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json +++ b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json @@ -6,7 +6,8 @@ "engine": "InnoDB", "field_order": [ "document_type", - "select_fields", + "date_fieldname", + "add_edit_fields", "selected_fields" ], "fields": [ @@ -18,22 +19,29 @@ "options": "DocType", "reqd": 1 }, - { - "fieldname": "select_fields", - "fieldtype": "Button", - "in_list_view": 1, - "label": "Select Fields" - }, { "fieldname": "selected_fields", "fieldtype": "Code", "label": "selected_fields" + }, + { + "fieldname": "add_edit_fields", + "fieldtype": "Button", + "in_list_view": 1, + "label": "Add / Edit Fields" + }, + { + "fieldname": "date_fieldname", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Date Fieldname", + "reqd": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-11-25 14:19:33.637543", + "modified": "2020-11-28 19:04:48.323164", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient History Custom Document Type", diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js index 155476e2b1..ca2707f6a6 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js @@ -73,13 +73,10 @@ frappe.ui.form.on('Patient History Settings', { }); frappe.ui.form.on('Patient History Custom Document Type', { - select_fields: function(frm) { - let doc = frm.selected_doc; - - if (!doc.document_type) - frappe.throw(__('Select the Document Type first.')) - - frm.events.field_selector(frm, doc); + add_edit_fields: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + if (row.document_type) { + frm.events.field_selector(frm, row); + } } - }); diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py index 27cbf2fc60..9e876e8c95 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py @@ -3,8 +3,21 @@ # For license information, please see license.txt from __future__ import unicode_literals -# import frappe +import frappe +from frappe import _ from frappe.model.document import Document class PatientHistorySettings(Document): - pass + def validate(self): + self.validate_date_fieldnames() + + def validate_date_fieldnames(self): + for entry in self.custom_doctypes: + field = frappe.get_meta(entry.document_type).get_field(entry.date_fieldname) + if not field: + frappe.throw(_('Row #{0}: No such Field named {1} found in the Document Type {2}.').format( + entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type))) + + if field.fieldtype not in ['Date', 'Datetime']: + frappe.throw(_('Row #{0}: Field {1} in Document Type {2} is not a Date / Datetime field.').format( + entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type))) \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json index ec40d893eb..ef4fc2bfe1 100644 --- a/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json +++ b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json @@ -6,7 +6,7 @@ "engine": "InnoDB", "field_order": [ "document_type", - "select_fields", + "add_edit_fields", "selected_fields" ], "fields": [ @@ -19,22 +19,22 @@ "read_only": 1, "reqd": 1 }, - { - "fieldname": "select_fields", - "fieldtype": "Button", - "in_list_view": 1, - "label": "Select Fields" - }, { "fieldname": "selected_fields", "fieldtype": "Code", "label": "Selected Fields" + }, + { + "fieldname": "add_edit_fields", + "fieldtype": "Button", + "in_list_view": 1, + "label": "Add / Edit Fields" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-11-25 14:19:53.708991", + "modified": "2020-11-28 18:57:30.446348", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient History Standard Document Type", From c91e03c8911286cb50731dd7064501b9c80474a9 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sat, 28 Nov 2020 20:24:06 +0530 Subject: [PATCH 136/728] feat: Create Patient Medical Record on configured doctype submission --- .../patient_history_settings.js | 6 ++- .../patient_history_settings.py | 53 ++++++++++++++++++- erpnext/hooks.py | 4 ++ 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js index ca2707f6a6..60926eeb11 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js @@ -40,7 +40,8 @@ frappe.ui.form.on('Patient History Settings', { if (field) { selected_fields.push({ label: field.label, - fieldname: field.fieldname + fieldname: field.fieldname, + fieldtype: field.fieldtype }); } } @@ -58,7 +59,8 @@ frappe.ui.form.on('Patient History Settings', { frappe.model.with_doctype(document_type, () => { // get doctype fields frappe.get_doc('DocType', document_type).fields.forEach(field => { - if (!in_list(frappe.model.no_value_type, field.fieldtype) && !field.hidden) { + if (!in_list(frappe.model.no_value_type, field.fieldtype) || + in_list(frappe.model.table_fields, field.fieldtype) && !field.hidden) { multiselect_fields.push({ label: field.label, value: field.fieldname, diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py index 9e876e8c95..af8c6f4557 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.utils import cstr from frappe.model.document import Document class PatientHistorySettings(Document): @@ -20,4 +21,54 @@ class PatientHistorySettings(Document): if field.fieldtype not in ['Date', 'Datetime']: frappe.throw(_('Row #{0}: Field {1} in Document Type {2} is not a Date / Datetime field.').format( - entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type))) \ No newline at end of file + entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type))) + + +def create_medical_record(doc, method=None): + if frappe.flags.in_patch or frappe.flags.in_install or frappe.flags.in_setup_wizard or \ + frappe.db.get_value('Doctype', doc.doctype, 'module') != 'Healthcare': + return + + subject = set_subject_field(doc) + date_field = get_date_field(doc.doctype) + medical_record = frappe.new_doc('Patient Medical Record') + medical_record.patient = doc.patient + medical_record.subject = subject + medical_record.status = 'Open' + medical_record.communication_date = doc.get(date_field) + medical_record.reference_doctype = doc.doctype + medical_record.reference_name = doc.name + medical_record.reference_owner = doc.owner + medical_record.save(ignore_permissions=True) + + +def set_subject_field(doc): + from frappe.utils.formatters import format_value + + meta = frappe.get_meta(doc.doctype) + subject = '' + patient_history_fields = get_patient_history_fields(doc) + + for entry in patient_history_fields: + fieldname = entry.get('fieldname') + if doc.get(fieldname): + formated_value = format_value(doc.get(fieldname), meta.get_field(fieldname), doc) + subject += frappe.bold(_(entry.get('label')) + ': ') + cstr(formated_value) + subject += '
' + + return subject + + +def get_date_field(doctype): + return frappe.db.get_value('Patient History Custom Document Type', + { 'document_type': doctype }, 'date_fieldname') + + +def get_patient_history_fields(doc): + import json + patient_history_fields = frappe.db.get_value('Patient History Custom Document Type', + { 'document_type': doc.doctype }, 'selected_fields') + + if patient_history_fields: + return json.loads(patient_history_fields) + diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 741176f33f..4ee42c7559 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -221,6 +221,10 @@ standard_queries = { } doc_events = { + "*": { + "on_submit": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.create_medical_record", + "on_cancel": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.delete_medical_record" + }, "Stock Entry": { "on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty", "on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty" From a75f79762f224f8b38d2877b7a596cc93d9647fd Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sat, 28 Nov 2020 23:09:07 +0530 Subject: [PATCH 137/728] feat: Link for individual documents in Patient History --- .../page/patient_history/patient_history.html | 1 - .../page/patient_history/patient_history.js | 19 ++++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/erpnext/healthcare/page/patient_history/patient_history.html b/erpnext/healthcare/page/patient_history/patient_history.html index 7a9446dffd..60f4501fed 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.html +++ b/erpnext/healthcare/page/patient_history/patient_history.html @@ -1,6 +1,5 @@
-

{%= __("Select Patient") %}

diff --git a/erpnext/healthcare/page/patient_history/patient_history.js b/erpnext/healthcare/page/patient_history/patient_history.js index fe5b7bc488..3e6d790ca7 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.js +++ b/erpnext/healthcare/page/patient_history/patient_history.js @@ -16,6 +16,8 @@ frappe.pages['patient_history'].on_page_load = function(wrapper) { fieldtype: "Link", options: "Patient", fieldname: "patient", + placeholder: __('Select Patient'), + only_select: true, change: function(){ if(pid != patient.get_value() && patient.get_value()){ me.start = 0; @@ -27,7 +29,6 @@ frappe.pages['patient_history'].on_page_load = function(wrapper) { pid = patient.get_value(); } }, - only_input: true, }); patient.refresh(); @@ -120,7 +121,11 @@ var add_to_records = function(me, data){ data[i].imgsrc = false; } var time_line_heading = data[i].practitioner ? `${data[i].practitioner} ` : ``; - time_line_heading += data[i].reference_doctype + " - "+ data[i].reference_name; + time_line_heading += data[i].reference_doctype + " - " + + ` + ${data[i].reference_name} + ` + details += `
  • `; @@ -135,7 +140,7 @@ var add_to_records = function(me, data){ } details += `
    - `+time_line_heading+` on + `+time_line_heading+` ${data[i].date_sep} @@ -172,11 +177,11 @@ var add_date_separator = function(data) { var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); if(diff < 1) { - var pdate = 'Today'; + var pdate = __('Today'); } else if(diff < 2) { - pdate = 'Yesterday'; + pdate = __('Yesterday'); } else { - pdate = frappe.datetime.global_date_format(date); + pdate = __('on ') + frappe.datetime.global_date_format(date); } data.date_sep = pdate; return data; @@ -227,7 +232,7 @@ var show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) { }, callback: function(r) { if (r.message){ - var show_chart_btns_html = "
    "); - me.page.main.find("."+docname).parent().find('.document-html').show(); - me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched', "1"); + if (r.message) { + me.page.main.find('.' + docname).hide(); + + me.page.main.find('.' + docname).parent().find('.document-html').html( + `${r.message.html} +
    + + +
    + `); + + me.page.main.find('.' + docname).parent().find('.document-html').show(); + me.page.main.find('.' + docname).parent().find('.document-html').attr('data-fetched', '1'); } - }, - freeze: true + } }); } } }); - this.page.main.on("click", ".btn-less", function() { - var docname = $(this).attr("data-docname"); - me.page.main.find("."+docname).parent().find('.document-id').show(); - me.page.main.find("."+docname).parent().find('.document-html').hide(); + this.page.main.on('click', '.btn-less', function() { + let docname = $(this).attr('data-docname'); + me.page.main.find('.' + docname).parent().find('.document-id').show(); + me.page.main.find('.' + docname).parent().find('.document-html').hide(); }); me.start = 0; - me.page.main.on("click", ".btn-get-records", function(){ + me.page.main.on('click', '.btn-get-records', function(){ get_documents(patient.get_value(), me); }); }; -var get_documents = function(patient, me){ +let get_documents = function(patient, me) { frappe.call({ - "method": "erpnext.healthcare.page.patient_history.patient_history.get_feed", + 'method': 'erpnext.healthcare.page.patient_history.patient_history.get_feed', args: { name: patient, start: me.start, page_length: 20 }, - callback: function (r) { - var data = r.message; - if(data.length){ + callback: function(r) { + let data = r.message; + if (data.length) { add_to_records(me, data); - }else{ - me.page.main.find(".patient_documents_list").append("


    No more records..

    "); - me.page.main.find(".btn-get-records").hide(); + } else { + me.page.main.find('.patient_documents_list').append(` +
    +

    ${__('No more records..')}

    +
    `); + me.page.main.find('.btn-get-records').hide(); } } }); }; -var add_to_records = function(me, data){ - var details = ""; - me.page.main.find(".patient_documents_list").append(details); + + details += ''; + me.page.main.find('.patient_documents_list').append(details); me.start += data.length; - if(data.length===20){ + + if (data.length === 20) { me.page.main.find(".btn-get-records").show(); - }else{ + } else { me.page.main.find(".btn-get-records").hide(); - me.page.main.find(".patient_documents_list").append("


    No more records..

    "); + me.page.main.find(".patient_documents_list").append(` +
    +

    ${__('No more records..')}

    +
    `); } }; -var add_date_separator = function(data) { - var date = frappe.datetime.str_to_obj(data.creation); +let add_date_separator = function(data) { + let date = frappe.datetime.str_to_obj(data.creation); + let pdate = ''; + let diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); - var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); - if(diff < 1) { - var pdate = __('Today'); - } else if(diff < 2) { + if (diff < 1) { + pdate = __('Today'); + } else if (diff < 2) { pdate = __('Yesterday'); } else { pdate = __('on ') + frappe.datetime.global_date_format(date); @@ -187,107 +212,118 @@ var add_date_separator = function(data) { return data; }; -var show_patient_info = function(patient, me){ +let show_patient_info = function(patient, me) { frappe.call({ - "method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail", + 'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail', args: { patient: patient }, - callback: function (r) { - var data = r.message; - var details = ""; - if(data.image){ - details += "
    "; + callback: function(r) { + let data = r.message; + let details = ''; + if (data.image){ + details += `
    `; } - details += "" + data.patient_name +"
    " + data.sex; - if(data.email) details += "
    " + data.email; - if(data.mobile) details += "
    " + data.mobile; - if(data.occupation) details += "

    Occupation : " + data.occupation; - if(data.blood_group) details += "
    Blood group : " + data.blood_group; - if(data.allergies) details += "

    Allergies : "+ data.allergies.replace("\n", "
    "); - if(data.medication) details += "
    Medication : "+ data.medication.replace("\n", "
    "); - if(data.alcohol_current_use) details += "

    Alcohol use : "+ data.alcohol_current_use; - if(data.alcohol_past_use) details += "
    Alcohol past use : "+ data.alcohol_past_use; - if(data.tobacco_current_use) details += "
    Tobacco use : "+ data.tobacco_current_use; - if(data.tobacco_past_use) details += "
    Tobacco past use : "+ data.tobacco_past_use; - if(data.medical_history) details += "

    Medical history : "+ data.medical_history.replace("\n", "
    "); - if(data.surgical_history) details += "
    Surgical history : "+ data.surgical_history.replace("\n", "
    "); - if(data.surrounding_factors) details += "

    Occupational hazards : "+ data.surrounding_factors.replace("\n", "
    "); - if(data.other_risk_factors) details += "
    Other risk factors : " + data.other_risk_factors.replace("\n", "
    "); - if(data.patient_details) details += "

    More info : " + data.patient_details.replace("\n", "
    "); - if(details){ - details = "
    " + details + "
    "; + details += ` ${data.patient_name}
    ${data.sex}`; + if (data.email) details += `
    ${data.email}` + if (data.mobile) details += `
    ${data.mobile}`; + if (data.occupation) details += `

    ${__('Occupation')} : ${data.occupation}`; + if (data.blood_group) details += `
    ${__('Blood Group')} : ${data.blood_group}`; + if (data.allergies) details += `

    ${__('Allerigies')} : ${data.allergies.replace("\n", ", ")}`; + if (data.medication) details += `
    ${__('Medication')} : ${data.medication.replace("\n", ", ")}`; + if (data.alcohol_current_use) details += `

    ${__('Alcohol use')} : ${data.alcohol_current_use}`; + if (data.alcohol_past_use) details += `
    ${__('Alcohol past use')} : ${data.alcohol_past_use}`; + if (data.tobacco_current_use) details += `
    ${__('Tobacco use')} : ${data.tobacco_current_use}`; + if (data.tobacco_past_use) details += `
    ${__('Tobacco past use')} : ${data.tobacco_past_use}`; + if (data.medical_history) details += `

    ${__('Medical history')} : ${data.medical_history.replace("\n", ", ")}`; + if (data.surgical_history) details += `
    ${__('Surgical history')} : ${data.surgical_history.replace("\n", ", ")}`; + if (data.surrounding_factors) details += `

    ${__('Occupational hazards')} : ${data.surrounding_factors.replace("\n", ", ")}`; + if (data.other_risk_factors) details += `
    ${__('Other risk factors')} : ${data.other_risk_factors.replace("\n", ", ")}`; + if (data.patient_details) details += `

    ${__('More info')} : ${data.patient_details.replace("\n", ", ")}`; + + if (details){ + details = `
    ` + details + `
    `; } - me.page.main.find(".patient_details").html(details); + me.page.main.find('.patient_details').html(details); } }); }; -var show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) { +let show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) { frappe.call({ - method: "erpnext.healthcare.utils.get_patient_vitals", + method: 'erpnext.healthcare.utils.get_patient_vitals', args:{ patient: patient }, callback: function(r) { - if (r.message){ - var show_chart_btns_html = ""; - me.page.main.find(".show_chart_btns").html(show_chart_btns_html); - var data = r.message; + if (r.message) { + let show_chart_btns_html = ` + `; + + me.page.main.find('.show_chart_btns').html(show_chart_btns_html); + let data = r.message; let labels = [], datasets = []; let bp_systolic = [], bp_diastolic = [], temperature = []; let pulse = [], respiratory_rate = [], bmi = [], height = [], weight = []; - for(var i=0; i d + ' ' + pts, } }); - }else{ - me.page.main.find(".patient_vital_charts").html(""); - me.page.main.find(".show_chart_btns").html(""); + } else { + me.page.main.find('.patient_vital_charts').html(''); + me.page.main.find('.show_chart_btns').html(''); } } }); From fc1e352adf3f7efbbf243a029714d3c8eef0720f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 29 Nov 2020 22:38:14 +0530 Subject: [PATCH 139/728] feat: Doctype filter for Patient History --- .../page/patient_history/patient_history.css | 5 ++ .../page/patient_history/patient_history.html | 8 +++ .../page/patient_history/patient_history.js | 58 ++++++++++++++---- .../page/patient_history/patient_history.py | 59 ++++++++++++------- 4 files changed, 98 insertions(+), 32 deletions(-) diff --git a/erpnext/healthcare/page/patient_history/patient_history.css b/erpnext/healthcare/page/patient_history/patient_history.css index 865d6abee0..1bb589164e 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.css +++ b/erpnext/healthcare/page/patient_history/patient_history.css @@ -109,6 +109,11 @@ padding-right: 0px; } +.patient-history-filter { + margin-left: 35px; + width: 25%; +} + #page-medical_record .plot-wrapper { padding: 20px 15px; border-bottom: 1px solid #d1d8dd; diff --git a/erpnext/healthcare/page/patient_history/patient_history.html b/erpnext/healthcare/page/patient_history/patient_history.html index 60f4501fed..00b38e7258 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.html +++ b/erpnext/healthcare/page/patient_history/patient_history.html @@ -10,6 +10,14 @@
    +
    +
    +
    +
    +
    +
    +
    +
    diff --git a/erpnext/healthcare/page/patient_history/patient_history.js b/erpnext/healthcare/page/patient_history/patient_history.js index 5f1851fb0f..40b86fdff4 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.js +++ b/erpnext/healthcare/page/patient_history/patient_history.js @@ -20,14 +20,16 @@ frappe.pages['patient_history'].on_page_load = function(wrapper) { placeholder: __('Select Patient'), only_select: true, change: function(){ - if (pid != patient.get_value() && patient.get_value()) { + let patient_id = patient.get_value(); + if (pid != patient_id && patient_id) { me.start = 0; me.page.main.find('.patient_documents_list').html(''); - get_documents(patient.get_value(), me); - show_patient_info(patient.get_value(), me); - show_patient_vital_charts(patient.get_value(), me, 'bp', 'mmHg', 'Blood Pressure'); + setup_filters(patient_id, me) + get_documents(patient_id, me); + show_patient_info(patient_id, me); + show_patient_vital_charts(patient_id, me, 'bp', 'mmHg', 'Blood Pressure'); } - pid = patient.get_value(); + pid = patient_id; } }, }); @@ -93,14 +95,48 @@ frappe.pages['patient_history'].on_page_load = function(wrapper) { }); }; -let get_documents = function(patient, me) { +let setup_filters = function(patient, me) { + frappe.xcall( + 'erpnext.healthcare.page.patient_history.patient_history.get_patient_history_doctypes' + ).then(document_types => { + let doctype_filter = frappe.ui.form.make_control({ + parent: $('.doctype-filter'), + df: { + fieldtype: 'MultiSelectList', + fieldname: 'document_type', + placeholder: __('Select Document Type'), + input_class: 'input-xs', + change: () => { + me.start = 0; + me.page.main.find('.patient_documents_list').html(''); + get_documents(patient, me, doctype_filter.get_value()); + }, + get_data: () => { + return document_types.map(document_type => { + return { + description: document_type, + value: document_type + }; + }); + }, + } + }); + doctype_filter.refresh(); + }) +} + +let get_documents = function(patient, me, document_types="") { + let filters = { + name: patient, + start: me.start, + page_length: 20 + }; + if (document_types) + filters['document_types'] = document_types; + frappe.call({ 'method': 'erpnext.healthcare.page.patient_history.patient_history.get_feed', - args: { - name: patient, - start: me.start, - page_length: 20 - }, + args: filters, callback: function(r) { let data = r.message; if (data.length) { diff --git a/erpnext/healthcare/page/patient_history/patient_history.py b/erpnext/healthcare/page/patient_history/patient_history.py index 772aa4ef5e..c04b376197 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.py +++ b/erpnext/healthcare/page/patient_history/patient_history.py @@ -4,36 +4,53 @@ from __future__ import unicode_literals import frappe +import json from frappe.utils import cint from erpnext.healthcare.utils import render_docs_as_html @frappe.whitelist() -def get_feed(name, start=0, page_length=20): +def get_feed(name, document_types=None, start=0, page_length=20): """get feed""" - result = frappe.db.sql("""select name, owner, creation, - reference_doctype, reference_name, subject - from `tabPatient Medical Record` - where patient=%(patient)s - order by creation desc - limit %(start)s, %(page_length)s""", - { - "patient": name, - "start": cint(start), - "page_length": cint(page_length) - }, as_dict=True) + filters = {'patient': name} + if document_types: + document_types = json.loads(document_types) + filters['reference_doctype'] = ['IN', document_types] + + result = frappe.db.get_all('Patient Medical Record', + fields=['name', 'owner', 'creation', + 'reference_doctype', 'reference_name', 'subject'], + filters=filters, + order_by='creation DESC', + limit=cint(page_length), + start=cint(start) + ) + return result @frappe.whitelist() def get_feed_for_dt(doctype, docname): """get feed""" - result = frappe.db.sql("""select name, owner, modified, creation, - reference_doctype, reference_name, subject - from `tabPatient Medical Record` - where reference_name=%(docname)s and reference_doctype=%(doctype)s - order by creation desc""", - { - "docname": docname, - "doctype": doctype - }, as_dict=True) + result = frappe.db.get_all('Patient Medical Record', + fields=['name', 'owner', 'creation', + 'reference_doctype', 'reference_name', 'subject'], + filters={ + 'reference_doctype': doctype, + 'reference_name': docname + }, + order_by='creation DESC' + ) return result + +@frappe.whitelist() +def get_patient_history_doctypes(): + document_types = [] + settings = frappe.get_single("Patient History Settings") + + for entry in settings.standard_doctypes: + document_types.append(entry.document_type) + + for entry in settings.custom_doctypes: + document_types.append(entry.document_type) + + return document_types \ No newline at end of file From 4af3d4e4bb745f35549ce3b638981206c1f84a3e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 29 Nov 2020 22:43:56 +0530 Subject: [PATCH 140/728] fix: feed not visible when filter is reset --- erpnext/healthcare/page/patient_history/patient_history.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/healthcare/page/patient_history/patient_history.py b/erpnext/healthcare/page/patient_history/patient_history.py index c04b376197..b8494c9e58 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.py +++ b/erpnext/healthcare/page/patient_history/patient_history.py @@ -14,7 +14,8 @@ def get_feed(name, document_types=None, start=0, page_length=20): filters = {'patient': name} if document_types: document_types = json.loads(document_types) - filters['reference_doctype'] = ['IN', document_types] + if len(document_types): + filters['reference_doctype'] = ['IN', document_types] result = frappe.db.get_all('Patient Medical Record', fields=['name', 'owner', 'creation', From 4d6d115a4d43aa3b7c6ebf2604998a2a4728050c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 29 Nov 2020 23:16:12 +0530 Subject: [PATCH 141/728] feat: date range filter for Patient History --- .../page/patient_history/patient_history.html | 3 +- .../page/patient_history/patient_history.js | 27 +++++++++++++++-- .../page/patient_history/patient_history.py | 30 ++++++++++++++----- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/erpnext/healthcare/page/patient_history/patient_history.html b/erpnext/healthcare/page/patient_history/patient_history.html index 00b38e7258..deaaa97868 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.html +++ b/erpnext/healthcare/page/patient_history/patient_history.html @@ -14,8 +14,7 @@
    -
    -
    +
    diff --git a/erpnext/healthcare/page/patient_history/patient_history.js b/erpnext/healthcare/page/patient_history/patient_history.js index 40b86fdff4..5a6295b707 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.js +++ b/erpnext/healthcare/page/patient_history/patient_history.js @@ -109,7 +109,7 @@ let setup_filters = function(patient, me) { change: () => { me.start = 0; me.page.main.find('.patient_documents_list').html(''); - get_documents(patient, me, doctype_filter.get_value()); + get_documents(patient, me, doctype_filter.get_value(), date_range_field.get_value()); }, get_data: () => { return document_types.map(document_type => { @@ -122,10 +122,29 @@ let setup_filters = function(patient, me) { } }); doctype_filter.refresh(); - }) + + let date_range_field = frappe.ui.form.make_control({ + df: { + fieldtype: 'DateRange', + fieldname: 'date_range', + placeholder: __('Date Range'), + input_class: 'input-xs', + change: () => { + let selected_date_range = date_range_field.get_value(); + if (selected_date_range && selected_date_range.length === 2) { + me.start = 0; + me.page.main.find('.patient_documents_list').html(''); + get_documents(patient, me, doctype_filter.get_value(), selected_date_range); + } + } + }, + parent: $('.date-filter') + }); + date_range_field.refresh(); + }); } -let get_documents = function(patient, me, document_types="") { +let get_documents = function(patient, me, document_types="", selected_date_range="") { let filters = { name: patient, start: me.start, @@ -133,6 +152,8 @@ let get_documents = function(patient, me, document_types="") { }; if (document_types) filters['document_types'] = document_types; + if (selected_date_range) + filters['date_range'] = selected_date_range; frappe.call({ 'method': 'erpnext.healthcare.page.patient_history.patient_history.get_feed', diff --git a/erpnext/healthcare/page/patient_history/patient_history.py b/erpnext/healthcare/page/patient_history/patient_history.py index b8494c9e58..de8a5771d2 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.py +++ b/erpnext/healthcare/page/patient_history/patient_history.py @@ -9,13 +9,9 @@ from frappe.utils import cint from erpnext.healthcare.utils import render_docs_as_html @frappe.whitelist() -def get_feed(name, document_types=None, start=0, page_length=20): +def get_feed(name, document_types=None, date_range=None, start=0, page_length=20): """get feed""" - filters = {'patient': name} - if document_types: - document_types = json.loads(document_types) - if len(document_types): - filters['reference_doctype'] = ['IN', document_types] + filters = get_filters(name, document_types, date_range) result = frappe.db.get_all('Patient Medical Record', fields=['name', 'owner', 'creation', @@ -28,6 +24,25 @@ def get_feed(name, document_types=None, start=0, page_length=20): return result + +def get_filters(name, document_types=None, date_range=None): + filters = {'patient': name} + if document_types: + document_types = json.loads(document_types) + if len(document_types): + filters['reference_doctype'] = ['IN', document_types] + + if date_range: + try: + date_range = json.loads(date_range) + if date_range: + filters['creation'] = ['between', [date_range[0], date_range[1]]] + except json.decoder.JSONDecodeError: + pass + + return filters + + @frappe.whitelist() def get_feed_for_dt(doctype, docname): """get feed""" @@ -43,6 +58,7 @@ def get_feed_for_dt(doctype, docname): return result + @frappe.whitelist() def get_patient_history_doctypes(): document_types = [] @@ -54,4 +70,4 @@ def get_patient_history_doctypes(): for entry in settings.custom_doctypes: document_types.append(entry.document_type) - return document_types \ No newline at end of file + return document_types From 452cbcd6eaa1d63538261ab91296b3cf2116ff79 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 30 Nov 2020 10:55:12 +0530 Subject: [PATCH 142/728] 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 06e7cc2c35bf526f5a7bfba81d645ed4028a276b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 30 Nov 2020 12:01:48 +0530 Subject: [PATCH 143/728] fix: Handle table field rendering in Patient Medical record --- .../patient_history_settings.py | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py index af8c6f4557..759fcadef2 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py @@ -51,10 +51,16 @@ def set_subject_field(doc): for entry in patient_history_fields: fieldname = entry.get('fieldname') - if doc.get(fieldname): - formated_value = format_value(doc.get(fieldname), meta.get_field(fieldname), doc) - subject += frappe.bold(_(entry.get('label')) + ': ') + cstr(formated_value) - subject += '
    ' + if entry.get('fieldtype') == 'Table' and doc.get(fieldname): + formatted_value = get_formatted_value_for_table_field(doc.get(fieldname), meta.get_field(fieldname)) + subject += frappe.bold(_(entry.get('label')) + ': ') + '
    ' + cstr(formatted_value) + + else: + if doc.get(fieldname): + formatted_value = format_value(doc.get(fieldname), meta.get_field(fieldname), doc) + subject += frappe.bold(_(entry.get('label')) + ': ') + cstr(formatted_value) + + subject += '
    ' return subject @@ -72,3 +78,27 @@ def get_patient_history_fields(doc): if patient_history_fields: return json.loads(patient_history_fields) + +def get_formatted_value_for_table_field(items, df): + child_meta = frappe.get_meta(df.options) + + table_head = '' + table_row = '' + html = '' + create_head = True + for item in items: + table_row += '' + for cdf in child_meta.fields: + if cdf.in_list_view: + if create_head: + table_head += '' + cdf.label + '' + if item.get(cdf.fieldname): + table_row += '' + str(item.get(cdf.fieldname)) + '' + else: + table_row += '' + create_head = False + table_row += '' + + html += "" + table_head + table_row + '
    ' + + return html From f3df5c9f3cec4db7092c301d03897eca3acc67e0 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 30 Nov 2020 12:16:28 +0530 Subject: [PATCH 144/728] feat: patch to setup standard doctype config in Patient History Settings --- .../healthcare/doctype/lab_test/lab_test.json | 6 +- .../patient_encounter/patient_encounter.json | 4 +- ...atient_history_standard_document_type.json | 11 ++- erpnext/healthcare/setup.py | 80 +++++++++++++++++++ erpnext/patches.txt | 1 + ..._history_settings_for_standard_doctypes.py | 9 +++ 6 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json index edf1d911aa..ac61fea3ad 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.json +++ b/erpnext/healthcare/doctype/lab_test/lab_test.json @@ -359,6 +359,7 @@ { "fieldname": "normal_test_items", "fieldtype": "Table", + "label": "Normal Test Result", "options": "Normal Test Result", "print_hide": 1 }, @@ -380,6 +381,7 @@ { "fieldname": "sensitivity_test_items", "fieldtype": "Table", + "label": "Sensitivity Test Result", "options": "Sensitivity Test Result", "print_hide": 1, "report_hide": 1 @@ -529,6 +531,7 @@ { "fieldname": "descriptive_test_items", "fieldtype": "Table", + "label": "Descriptive Test Result", "options": "Descriptive Test Result", "print_hide": 1, "report_hide": 1 @@ -549,13 +552,14 @@ { "fieldname": "organism_test_items", "fieldtype": "Table", + "label": "Organism Test Result", "options": "Organism Test Result", "print_hide": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-07-30 18:18:38.516215", + "modified": "2020-11-30 11:04:17.195848", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test", diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index 15675f4673..b646ff9ebe 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -210,7 +210,7 @@ { "fieldname": "drug_prescription", "fieldtype": "Table", - "label": "Items", + "label": "Drug Prescription", "options": "Drug Prescription" }, { @@ -328,7 +328,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-16 21:00:08.644531", + "modified": "2020-11-30 10:39:00.783119", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Encounter", diff --git a/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json index ef4fc2bfe1..9c9d0cb4cd 100644 --- a/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json +++ b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json @@ -6,6 +6,7 @@ "engine": "InnoDB", "field_order": [ "document_type", + "date_fieldname", "add_edit_fields", "selected_fields" ], @@ -29,12 +30,20 @@ "fieldtype": "Button", "in_list_view": 1, "label": "Add / Edit Fields" + }, + { + "fieldname": "date_fieldname", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Date Fieldname", + "read_only": 1, + "reqd": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-11-28 18:57:30.446348", + "modified": "2020-11-30 12:15:14.940935", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient History Standard Document Type", diff --git a/erpnext/healthcare/setup.py b/erpnext/healthcare/setup.py index 06840801d3..bf4df7e4c8 100644 --- a/erpnext/healthcare/setup.py +++ b/erpnext/healthcare/setup.py @@ -16,6 +16,7 @@ def setup_healthcare(): create_healthcare_item_groups() create_sensitivity() add_healthcare_service_unit_tree_root() + setup_patient_history_settings() def create_medical_departments(): departments = [ @@ -213,3 +214,82 @@ def get_company(): if company: return company[0].name return None + +def setup_patient_history_settings(): + import json + + settings = frappe.get_single('Patient History Settings') + configuration = get_patient_history_config() + for dt, config in configuration.items(): + settings.append("standard_doctypes", { + "document_type": dt, + "date_fieldname": config[0], + "selected_fields": json.dumps(config[1]) + }) + settings.save() + +def get_patient_history_config(): + return { + "Patient Encounter": ("encounter_date", [ + {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"}, + {"label": "Symptoms", "fieldname": "symptoms", "fieldtype": "Table Multiselect"}, + {"label": "Diagnosis", "fieldname": "diagnosis", "fieldtype": "Table Multiselect"}, + {"label": "Drug Prescription", "fieldname": "drug_prescription", "fieldtype": "Table"}, + {"label": "Lab Tests", "fieldname": "lab_test_prescription", "fieldtype": "Table"}, + {"label": "Clinical Procedures", "fieldname": "procedure_prescription", "fieldtype": "Table"}, + {"label": "Therapies", "fieldname": "therapies", "fieldtype": "Table"}, + {"label": "Review Details", "fieldname": "encounter_comment", "fieldtype": "Small Text"} + ]), + "Clinical Procedure": ("start_date", [ + {"label": "Procedure Template", "fieldname": "procedure_template", "fieldtype": "Link"}, + {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"}, + {"label": "Notes", "fieldname": "notes", "fieldtype": "Small Text"}, + {"label": "Service Unit", "fieldname": "service_unit", "fieldtype": "Healthcare Service Unit"}, + {"label": "Start Time", "fieldname": "start_time", "fieldtype": "Time"}, + {"label": "Sample", "fieldname": "sample", "fieldtype": "Link"} + ]), + "Lab Test": ("result_date", [ + {"label": "Test Template", "fieldname": "template", "fieldtype": "Link"}, + {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"}, + {"label": "Test Name", "fieldname": "lab_test_name", "fieldtype": "Data"}, + {"label": "Lab Technician Name", "fieldname": "employee_name", "fieldtype": "Data"}, + {"label": "Sample ID", "fieldname": "sample", "fieldtype": "Link"}, + {"label": "Normal Test Result", "fieldname": "normal_test_items", "fieldtype": "Table"}, + {"label": "Descriptive Test Result", "fieldname": "descriptive_test_items", "fieldtype": "Table"}, + {"label": "Organism Test Result", "fieldname": "organism_test_items", "fieldtype": "Table"}, + {"label": "Sensitivity Test Result", "fieldname": "sensitivity_test_items", "fieldtype": "Table"}, + {"label": "Comments", "fieldname": "lab_test_comment", "fieldtype": "Table"} + ]), + "Therapy Session": ("start_date", [ + {"label": "Therapy Type", "fieldname": "therapy_type", "fieldtype": "Link"}, + {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"}, + {"label": "Therapy Plan", "fieldname": "therapy_plan", "fieldtype": "Link"}, + {"label": "Duration", "fieldname": "duration", "fieldtype": "Int"}, + {"label": "Location", "fieldname": "location", "fieldtype": "Link"}, + {"label": "Healthcare Service Unit", "fieldname": "service_unit", "fieldtype": "Link"}, + {"label": "Start Time", "fieldname": "start_time", "fieldtype": "Time"}, + {"label": "Exercises", "fieldname": "exercises", "fieldtype": "Table"}, + {"label": "Total Counts Targeted", "fieldname": "total_counts_targeted", "fieldtype": "Int"}, + {"label": "Total Counts Completed", "fieldname": "total_counts_completed", "fieldtype": "Int"} + ]), + "Vital Signs": ("signs_date", [ + {"label": "Body Temperature", "fieldname": "temperature", "fieldtype": "Data"}, + {"label": "Heart Rate / Pulse", "fieldname": "pulse", "fieldtype": "Data"}, + {"label": "Respiratory rate", "fieldname": "respiratory_rate", "fieldtype": "Data"}, + {"label": "Tongue", "fieldname": "tongue", "fieldtype": "Select"}, + {"label": "Abdomen", "fieldname": "abdomen", "fieldtype": "Select"}, + {"label": "Reflexes", "fieldname": "reflexes", "fieldtype": "Select"}, + {"label": "Blood Pressure", "fieldname": "bp", "fieldtype": "Data"}, + {"label": "Notes", "fieldname": "vital_signs_note", "fieldtype": "Small Text"}, + {"label": "Height (In Meter)", "fieldname": "height", "fieldtype": "Float"}, + {"label": "Weight (In Kilogram)", "fieldname": "weight", "fieldtype": "Float"}, + {"label": "BMI", "fieldname": "bmi", "fieldtype": "Float"} + ]), + "Inpatient Medication Order": ("start_date", [ + {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"}, + {"label": "Start Date", "fieldname": "start_date", "fieldtype": "Date"}, + {"label": "End Date", "fieldname": "end_date", "fieldtype": "Date"}, + {"label": "Medication Orders", "fieldname": "medication_orders", "fieldtype": "Table"}, + {"label": "Total Orders", "fieldname": "total_orders", "fieldtype": "Float"} + ]) + } \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 25be884117..fcb63ed6e4 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.setup_patient_history_settings_for_standard_doctypes diff --git a/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py b/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py new file mode 100644 index 0000000000..3332be0561 --- /dev/null +++ b/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py @@ -0,0 +1,9 @@ +from __future__ import unicode_literals +import frappe +from erpnext.healthcare.setup import setup_patient_history_settings + +def execute(): + if 'Healthcare' not in frappe.get_active_domains(): + return + + setup_patient_history_settings() \ No newline at end of file From 4097e89f8b80140389eec6b29aea337d3a7835f0 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 30 Nov 2020 12:48:39 +0530 Subject: [PATCH 145/728] feat: Standard doctype config for Patient History --- .../patient_history_settings.js | 17 ++++++++++++-- .../patient_history_settings.py | 22 ++++++++++++++----- erpnext/hooks.py | 1 + 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js index 60926eeb11..c3d0dce675 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js @@ -13,7 +13,7 @@ frappe.ui.form.on('Patient History Settings', { }); }, - field_selector: function(frm, doc) { + field_selector: function(frm, doc, standard=1) { let document_fields = (JSON.parse(doc.selected_fields)).map(f => f.fieldname); let d = new frappe.ui.Dialog({ title: __('{0} Fields', [__(doc.document_type)]), @@ -45,8 +45,12 @@ frappe.ui.form.on('Patient History Settings', { }); } } + let doctype = 'Patient History Custom Document Type'; + if (standard) + doctype = 'Patient History Standard Document Type'; - frappe.model.set_value('Patient History Custom Document Type', doc.name, 'selected_fields', JSON.stringify(selected_fields)); + + frappe.model.set_value(doctype, doc.name, 'selected_fields', JSON.stringify(selected_fields)); d.hide(); }); @@ -75,6 +79,15 @@ frappe.ui.form.on('Patient History Settings', { }); frappe.ui.form.on('Patient History Custom Document Type', { + add_edit_fields: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + if (row.document_type) { + frm.events.field_selector(frm, row, standard=0); + } + } +}); + +frappe.ui.form.on('Patient History Standard Document Type', { add_edit_fields: function(frm, cdt, cdn) { let row = locals[cdt][cdn]; if (row.document_type) { diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py index 759fcadef2..20b062e909 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py @@ -7,6 +7,7 @@ import frappe from frappe import _ from frappe.utils import cstr from frappe.model.document import Document +from erpnext.healthcare.page.patient_history.patient_history import get_patient_history_doctypes class PatientHistorySettings(Document): def validate(self): @@ -29,6 +30,9 @@ def create_medical_record(doc, method=None): frappe.db.get_value('Doctype', doc.doctype, 'module') != 'Healthcare': return + if doc.doctype not in get_patient_history_doctypes(): + return + subject = set_subject_field(doc) date_field = get_date_field(doc.doctype) medical_record = frappe.new_doc('Patient Medical Record') @@ -66,14 +70,15 @@ def set_subject_field(doc): def get_date_field(doctype): - return frappe.db.get_value('Patient History Custom Document Type', - { 'document_type': doctype }, 'date_fieldname') + dt = get_patient_history_config_dt(doctype) + + return frappe.db.get_value(dt, { 'document_type': doctype }, 'date_fieldname') def get_patient_history_fields(doc): import json - patient_history_fields = frappe.db.get_value('Patient History Custom Document Type', - { 'document_type': doc.doctype }, 'selected_fields') + dt = get_patient_history_config_dt(doc.doctype) + patient_history_fields = frappe.db.get_value(dt, { 'document_type': doc.doctype }, 'selected_fields') if patient_history_fields: return json.loads(patient_history_fields) @@ -99,6 +104,13 @@ def get_formatted_value_for_table_field(items, df): create_head = False table_row += '' - html += "" + table_head + table_row + '
    ' + html += "" + table_head + table_row + "
    " return html + + +def get_patient_history_config_dt(doctype): + if frappe.db.get_value('DocType', doctype, 'custom'): + return 'Patient History Custom Document Type' + else: + return 'Patient History Standard Document Type' diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 4ee42c7559..aa5291aa0e 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -223,6 +223,7 @@ standard_queries = { doc_events = { "*": { "on_submit": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.create_medical_record", + "on_cancel": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.update_medical_record", "on_cancel": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.delete_medical_record" }, "Stock Entry": { From 724e16bca120954049ba4a9532ef25b6446ddf19 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 30 Nov 2020 12:42:25 +0530 Subject: [PATCH 146/728] 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 c0fcc807d338ad614aa76f7ea03d2b804760da77 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 30 Nov 2020 13:22:01 +0530 Subject: [PATCH 147/728] feat: hooks for updating and deleting medical records --- .../patient_history_settings.py | 41 +++++++++++++++++-- erpnext/hooks.py | 2 +- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py index 20b062e909..367c34f1e8 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py @@ -26,11 +26,11 @@ class PatientHistorySettings(Document): def create_medical_record(doc, method=None): - if frappe.flags.in_patch or frappe.flags.in_install or frappe.flags.in_setup_wizard or \ - frappe.db.get_value('Doctype', doc.doctype, 'module') != 'Healthcare': + medical_record_required = validate_medical_record_required(doc) + if not medical_record_required: return - if doc.doctype not in get_patient_history_doctypes(): + if frappe.db.exists('Patient Medical Record', { 'reference_name': doc.name }): return subject = set_subject_field(doc) @@ -46,6 +46,30 @@ def create_medical_record(doc, method=None): medical_record.save(ignore_permissions=True) +def update_medical_record(doc, method=None): + medical_record_required = validate_medical_record_required(doc) + if not medical_record_required: + return + + medical_record_id = frappe.db.exists('Patient Medical Record', { 'reference_name': doc.name }) + + if medical_record_id: + subject = set_subject_field(doc) + frappe.db.set_value('Patient Medical Record', medical_record_id[0][0], 'subject', subject) + else: + create_medical_record(doc) + + +def delete_medical_record(doc, method=None): + medical_record_required = validate_medical_record_required(doc) + if not medical_record_required: + return + + record = frappe.db.exists('Patient Medical Record', { 'reference_name': doc.name }) + if record: + frappe.delete_doc('Patient Medical Record', record, force=1) + + def set_subject_field(doc): from frappe.utils.formatters import format_value @@ -114,3 +138,14 @@ def get_patient_history_config_dt(doctype): return 'Patient History Custom Document Type' else: return 'Patient History Standard Document Type' + + +def validate_medical_record_required(doc): + if frappe.flags.in_patch or frappe.flags.in_install or frappe.flags.in_setup_wizard or \ + frappe.db.get_value('Doctype', doc.doctype, 'module') != 'Healthcare': + return False + + if doc.doctype not in get_patient_history_doctypes(): + return False + + return True \ No newline at end of file diff --git a/erpnext/hooks.py b/erpnext/hooks.py index aa5291aa0e..51c169f400 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -223,7 +223,7 @@ standard_queries = { doc_events = { "*": { "on_submit": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.create_medical_record", - "on_cancel": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.update_medical_record", + "on_update": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.update_medical_record", "on_cancel": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.delete_medical_record" }, "Stock Entry": { From c538a4a31d2c9f829a308e03a01d9eac5d537d0e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 30 Nov 2020 13:22:34 +0530 Subject: [PATCH 148/728] refactor: remove medical record creation methods from individual doctypes --- .../clinical_procedure/clinical_procedure.py | 19 ------ .../healthcare/doctype/lab_test/lab_test.py | 56 ----------------- .../patient_encounter/patient_encounter.py | 62 +------------------ .../therapy_session/therapy_session.py | 21 ------- .../doctype/vital_signs/vital_signs.py | 40 ------------ 5 files changed, 1 insertion(+), 197 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index e55a1433a5..c324228467 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -100,7 +100,6 @@ class ClinicalProcedure(Document): allow_start = self.set_actual_qty() if allow_start: self.db_set('status', 'In Progress') - insert_clinical_procedure_to_medical_record(self) return 'success' return 'insufficient stock' @@ -247,21 +246,3 @@ def make_procedure(source_name, target_doc=None): }, target_doc, set_missing_values) return doc - - -def insert_clinical_procedure_to_medical_record(doc): - subject = frappe.bold(_("Clinical Procedure conducted: ")) + cstr(doc.procedure_template) + "
    " - if doc.practitioner: - subject += frappe.bold(_('Healthcare Practitioner: ')) + doc.practitioner - if subject and doc.notes: - subject += '
    ' + doc.notes - - medical_record = frappe.new_doc('Patient Medical Record') - medical_record.patient = doc.patient - medical_record.subject = subject - medical_record.status = 'Open' - medical_record.communication_date = doc.start_date - medical_record.reference_doctype = 'Clinical Procedure' - medical_record.reference_name = doc.name - medical_record.reference_owner = doc.owner - medical_record.save(ignore_permissions=True) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 2db7743865..4b57cd073d 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -17,11 +17,9 @@ class LabTest(Document): self.validate_result_values() self.db_set('submitted_date', getdate()) self.db_set('status', 'Completed') - insert_lab_test_to_medical_record(self) def on_cancel(self): self.db_set('status', 'Cancelled') - delete_lab_test_from_medical_record(self) self.reload() def on_update(self): @@ -330,60 +328,6 @@ def get_employee_by_user_id(user_id): return frappe.get_doc('Employee', emp_id) return None -def insert_lab_test_to_medical_record(doc): - table_row = False - subject = cstr(doc.lab_test_name) - if doc.practitioner: - subject += frappe.bold(_('Healthcare Practitioner: '))+ doc.practitioner + '
    ' - if doc.normal_test_items: - item = doc.normal_test_items[0] - comment = '' - if item.lab_test_comment: - comment = str(item.lab_test_comment) - table_row = frappe.bold(_('Lab Test Conducted: ')) + item.lab_test_name - - if item.lab_test_event: - table_row += frappe.bold(_('Lab Test Event: ')) + item.lab_test_event - - if item.result_value: - table_row += ' ' + frappe.bold(_('Lab Test Result: ')) + item.result_value - - if item.normal_range: - table_row += ' ' + _('Normal Range: ') + item.normal_range - table_row += ' ' + comment - - elif doc.descriptive_test_items: - item = doc.descriptive_test_items[0] - - if item.lab_test_particulars and item.result_value: - table_row = item.lab_test_particulars + ' ' + item.result_value - - elif doc.sensitivity_test_items: - item = doc.sensitivity_test_items[0] - - if item.antibiotic and item.antibiotic_sensitivity: - table_row = item.antibiotic + ' ' + item.antibiotic_sensitivity - - if table_row: - subject += '
    ' + table_row - if doc.lab_test_comment: - subject += '
    ' + cstr(doc.lab_test_comment) - - medical_record = frappe.new_doc('Patient Medical Record') - medical_record.patient = doc.patient - medical_record.subject = subject - medical_record.status = 'Open' - medical_record.communication_date = doc.result_date - medical_record.reference_doctype = 'Lab Test' - medical_record.reference_name = doc.name - medical_record.reference_owner = doc.owner - medical_record.save(ignore_permissions = True) - -def delete_lab_test_from_medical_record(self): - medical_record_id = frappe.db.sql('select name from `tabPatient Medical Record` where reference_name=%s', (self.name)) - - if medical_record_id and medical_record_id[0][0]: - frappe.delete_doc('Patient Medical Record', medical_record_id[0][0]) @frappe.whitelist() def get_lab_test_prescribed(patient): diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py index 87f42491fc..cc2141790f 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py @@ -17,10 +17,6 @@ class PatientEncounter(Document): def on_update(self): if self.appointment: frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed') - update_encounter_medical_record(self) - - def after_insert(self): - insert_encounter_to_medical_record(self) def on_submit(self): if self.therapies: @@ -33,8 +29,6 @@ class PatientEncounter(Document): if self.inpatient_record and self.drug_prescription: delete_ip_medication_order(self) - delete_medical_record(self) - def set_title(self): self.title = _('{0} with {1}').format(self.patient_name or self.patient, self.practitioner_name or self.practitioner)[:100] @@ -102,61 +96,7 @@ def create_therapy_plan(encounter): frappe.msgprint(_('Therapy Plan {0} created successfully.').format(frappe.bold(doc.name)), alert=True) -def insert_encounter_to_medical_record(doc): - subject = set_subject_field(doc) - medical_record = frappe.new_doc('Patient Medical Record') - medical_record.patient = doc.patient - medical_record.subject = subject - medical_record.status = 'Open' - medical_record.communication_date = doc.encounter_date - medical_record.reference_doctype = 'Patient Encounter' - medical_record.reference_name = doc.name - medical_record.reference_owner = doc.owner - medical_record.save(ignore_permissions=True) - - -def update_encounter_medical_record(encounter): - medical_record_id = frappe.db.exists('Patient Medical Record', {'reference_name': encounter.name}) - - if medical_record_id and medical_record_id[0][0]: - subject = set_subject_field(encounter) - frappe.db.set_value('Patient Medical Record', medical_record_id[0][0], 'subject', subject) - else: - insert_encounter_to_medical_record(encounter) - - -def delete_medical_record(encounter): - record = frappe.db.exists('Patient Medical Record', {'reference_name', encounter.name}) - if record: - frappe.delete_doc('Patient Medical Record', record, force=1) - def delete_ip_medication_order(encounter): record = frappe.db.exists('Inpatient Medication Order', {'patient_encounter': encounter.name}) if record: - frappe.delete_doc('Inpatient Medication Order', record, force=1) - - -def set_subject_field(encounter): - subject = frappe.bold(_('Healthcare Practitioner: ')) + encounter.practitioner + '
    ' - if encounter.symptoms: - subject += frappe.bold(_('Symptoms: ')) + '
    ' - for entry in encounter.symptoms: - subject += cstr(entry.complaint) + '
    ' - else: - subject += frappe.bold(_('No Symptoms')) + '
    ' - - if encounter.diagnosis: - subject += frappe.bold(_('Diagnosis: ')) + '
    ' - for entry in encounter.diagnosis: - subject += cstr(entry.diagnosis) + '
    ' - else: - subject += frappe.bold(_('No Diagnosis')) + '
    ' - - if encounter.drug_prescription: - subject += '
    ' + _('Drug(s) Prescribed.') - if encounter.lab_test_prescription: - subject += '
    ' + _('Test(s) Prescribed.') - if encounter.procedure_prescription: - subject += '
    ' + _('Procedure(s) Prescribed.') - - return subject + frappe.delete_doc('Inpatient Medication Order', record, force=1) \ No newline at end of file diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.py b/erpnext/healthcare/doctype/therapy_session/therapy_session.py index 85d0970177..f8a8e0c8a1 100644 --- a/erpnext/healthcare/doctype/therapy_session/therapy_session.py +++ b/erpnext/healthcare/doctype/therapy_session/therapy_session.py @@ -41,7 +41,6 @@ class TherapySession(Document): def on_submit(self): self.update_sessions_count_in_therapy_plan() - insert_session_medical_record(self) def on_cancel(self): self.update_sessions_count_in_therapy_plan(on_cancel=True) @@ -135,23 +134,3 @@ def get_therapy_item(therapy, item): item.reference_dt = 'Therapy Session' item.reference_dn = therapy.name return item - - -def insert_session_medical_record(doc): - subject = frappe.bold(_('Therapy: ')) + cstr(doc.therapy_type) + '
    ' - if doc.therapy_plan: - subject += frappe.bold(_('Therapy Plan: ')) + cstr(doc.therapy_plan) + '
    ' - if doc.practitioner: - subject += frappe.bold(_('Healthcare Practitioner: ')) + doc.practitioner - subject += frappe.bold(_('Total Counts Targeted: ')) + cstr(doc.total_counts_targeted) + '
    ' - subject += frappe.bold(_('Total Counts Completed: ')) + cstr(doc.total_counts_completed) + '
    ' - - medical_record = frappe.new_doc('Patient Medical Record') - medical_record.patient = doc.patient - medical_record.subject = subject - medical_record.status = 'Open' - medical_record.communication_date = doc.start_date - medical_record.reference_doctype = 'Therapy Session' - medical_record.reference_name = doc.name - medical_record.reference_owner = doc.owner - medical_record.save(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.py b/erpnext/healthcare/doctype/vital_signs/vital_signs.py index 69d81ff4b0..35c823d739 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.py +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.py @@ -12,47 +12,7 @@ class VitalSigns(Document): def validate(self): self.set_title() - def on_submit(self): - insert_vital_signs_to_medical_record(self) - - def on_cancel(self): - delete_vital_signs_from_medical_record(self) - def set_title(self): self.title = _('{0} on {1}').format(self.patient_name or self.patient, frappe.utils.format_date(self.signs_date))[:100] -def insert_vital_signs_to_medical_record(doc): - subject = set_subject_field(doc) - medical_record = frappe.new_doc('Patient Medical Record') - medical_record.patient = doc.patient - medical_record.subject = subject - medical_record.status = 'Open' - medical_record.communication_date = doc.signs_date - medical_record.reference_doctype = 'Vital Signs' - medical_record.reference_name = doc.name - medical_record.reference_owner = doc.owner - medical_record.flags.ignore_mandatory = True - medical_record.save(ignore_permissions=True) - -def delete_vital_signs_from_medical_record(doc): - medical_record = frappe.db.get_value('Patient Medical Record', {'reference_name': doc.name}) - if medical_record: - frappe.delete_doc('Patient Medical Record', medical_record) - -def set_subject_field(doc): - subject = '' - if doc.temperature: - subject += frappe.bold(_('Temperature: ')) + cstr(doc.temperature) + '
    ' - if doc.pulse: - subject += frappe.bold(_('Pulse: ')) + cstr(doc.pulse) + '
    ' - if doc.respiratory_rate: - subject += frappe.bold(_('Respiratory Rate: ')) + cstr(doc.respiratory_rate) + '
    ' - if doc.bp: - subject += frappe.bold(_('BP: ')) + cstr(doc.bp) + '
    ' - if doc.bmi: - subject += frappe.bold(_('BMI: ')) + cstr(doc.bmi) + '
    ' - if doc.nutrition_note: - subject += frappe.bold(_('Note: ')) + cstr(doc.nutrition_note) + '
    ' - - return subject From 5e3c51bf7d157a34baed3e791f8bb40052587654 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 30 Nov 2020 13:35:00 +0530 Subject: [PATCH 149/728] refactor: format value using standard formatters --- erpnext/healthcare/utils.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 96282f50a9..6b495a4eac 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import math import frappe from frappe import _ +from frappe.utils.formatters import format_value from frappe.utils import time_diff_in_hours, rounded from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity @@ -642,11 +643,15 @@ def render_doc_as_html(doctype, docname, exclude_fields = []): html += "" \ + table_head + table_row + "
    " continue + #on other field types add label and value to html if not df.hidden and not df.print_hide and doc.get(df.fieldname) and df.fieldname not in exclude_fields: - html += '
    {0} : {1}'.format(df.label or df.fieldname, \ - doc.get(df.fieldname)) + if doc.get(df.fieldname): + formatted_value = format_value(doc.get(df.fieldname), meta.get_field(df.fieldname), doc) + html += '
    {0} : {1}'.format(df.label or df.fieldname, formatted_value) + if not has_data : has_data = True + if sec_on and col_on and has_data: doc_html += section_html + html + '
    ' elif sec_on and not col_on and has_data: From 4ee293d2f45c257fa4b103adc1af088e68d4dd5f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 30 Nov 2020 14:12:48 +0530 Subject: [PATCH 150/728] fix: handle non-configured fields --- .../patient_history_custom_document_type.json | 5 +++-- .../patient_history_settings.js | 12 ++++++++---- .../patient_history_standard_document_type.json | 5 +++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json index 7986e48ced..3025c7b06d 100644 --- a/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json +++ b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json @@ -22,7 +22,8 @@ { "fieldname": "selected_fields", "fieldtype": "Code", - "label": "selected_fields" + "label": "Selected Fields", + "read_only": 1 }, { "fieldname": "add_edit_fields", @@ -41,7 +42,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-11-28 19:04:48.323164", + "modified": "2020-11-30 13:54:37.474671", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient History Custom Document Type", diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js index c3d0dce675..ee363462ef 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js @@ -14,7 +14,11 @@ frappe.ui.form.on('Patient History Settings', { }, field_selector: function(frm, doc, standard=1) { - let document_fields = (JSON.parse(doc.selected_fields)).map(f => f.fieldname); + let document_fields = []; + if (doc.selected_fields) + document_fields = (JSON.parse(doc.selected_fields)).map(f => f.fieldname); + + let doctype_fields = frm.events.get_doctype_fields(frm, doc.document_type, document_fields); let d = new frappe.ui.Dialog({ title: __('{0} Fields', [__(doc.document_type)]), fields: [ @@ -22,7 +26,7 @@ frappe.ui.form.on('Patient History Settings', { label: __('Select Fields'), fieldtype: 'MultiCheck', fieldname: 'fields', - options: frm.events.get_doctype_fields(frm, doc.document_type, document_fields), + options: doctype_fields, columns: 2 } ] @@ -49,7 +53,7 @@ frappe.ui.form.on('Patient History Settings', { if (standard) doctype = 'Patient History Standard Document Type'; - + d.refresh(); frappe.model.set_value(doctype, doc.name, 'selected_fields', JSON.stringify(selected_fields)); d.hide(); }); @@ -82,7 +86,7 @@ frappe.ui.form.on('Patient History Custom Document Type', { add_edit_fields: function(frm, cdt, cdn) { let row = locals[cdt][cdn]; if (row.document_type) { - frm.events.field_selector(frm, row, standard=0); + frm.events.field_selector(frm, row, 0); } } }); diff --git a/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json index 9c9d0cb4cd..b43099c4ea 100644 --- a/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json +++ b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json @@ -23,7 +23,8 @@ { "fieldname": "selected_fields", "fieldtype": "Code", - "label": "Selected Fields" + "label": "Selected Fields", + "read_only": 1 }, { "fieldname": "add_edit_fields", @@ -43,7 +44,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-11-30 12:15:14.940935", + "modified": "2020-11-30 13:54:56.773325", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient History Standard Document Type", From ed3fc20731ae7e2279fabf6274a7660ca520a2e7 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 30 Nov 2020 14:56:24 +0530 Subject: [PATCH 151/728] fix: sider issues --- .../page/patient_history/patient_history.js | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/erpnext/healthcare/page/patient_history/patient_history.js b/erpnext/healthcare/page/patient_history/patient_history.js index 5a6295b707..d509ea22a2 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.js +++ b/erpnext/healthcare/page/patient_history/patient_history.js @@ -24,7 +24,7 @@ frappe.pages['patient_history'].on_page_load = function(wrapper) { if (pid != patient_id && patient_id) { me.start = 0; me.page.main.find('.patient_documents_list').html(''); - setup_filters(patient_id, me) + setup_filters(patient_id, me); get_documents(patient_id, me); show_patient_info(patient_id, me); show_patient_vital_charts(patient_id, me, 'bp', 'mmHg', 'Blood Pressure'); @@ -90,7 +90,7 @@ frappe.pages['patient_history'].on_page_load = function(wrapper) { me.page.main.find('.' + docname).parent().find('.document-html').hide(); }); me.start = 0; - me.page.main.on('click', '.btn-get-records', function(){ + me.page.main.on('click', '.btn-get-records', function() { get_documents(patient.get_value(), me); }); }; @@ -142,7 +142,7 @@ let setup_filters = function(patient, me) { }); date_range_field.refresh(); }); -} +}; let get_documents = function(patient, me, document_types="", selected_date_range="") { let filters = { @@ -176,7 +176,7 @@ let get_documents = function(patient, me, document_types="", selected_date_range let add_to_records = function(me, data) { let details = "
    `; } details += ` ${data.patient_name}
    ${data.sex}`; - if (data.email) details += `
    ${data.email}` + if (data.email) details += `
    ${data.email}`; if (data.mobile) details += `
    ${data.mobile}`; if (data.occupation) details += `

    ${__('Occupation')} : ${data.occupation}`; if (data.blood_group) details += `
    ${__('Blood Group')} : ${data.blood_group}`; @@ -299,7 +299,7 @@ let show_patient_info = function(patient, me) { if (data.other_risk_factors) details += `
    ${__('Other risk factors')} : ${data.other_risk_factors.replace("\n", ", ")}`; if (data.patient_details) details += `

    ${__('More info')} : ${data.patient_details.replace("\n", ", ")}`; - if (details){ + if (details) { details = `
    ` + details + `
    `; } me.page.main.find('.patient_details').html(details); @@ -337,7 +337,7 @@ let show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) { let bp_systolic = [], bp_diastolic = [], temperature = []; let pulse = [], respiratory_rate = [], bmi = [], height = [], weight = []; - for(let i=0; i Date: Thu, 12 Nov 2020 16:57:42 +0530 Subject: [PATCH 152/728] 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 153/728] 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 154/728] 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 155/728] 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 156/728] 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 157/728] 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 158/728] 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 159/728] 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 160/728] 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 161/728] 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 162/728] 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 163/728] 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 164/728] 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 165/728] 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 166/728] 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 167/728] 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 168/728] 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 169/728] 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 170/728] 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 171/728] 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 172/728] 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 0d43ea5ebb8fd84861b984491b5d61b8597f54e3 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 30 Nov 2020 15:00:42 +0530 Subject: [PATCH 173/728] refactor: show Patient History feed as per configured date instead of creation --- .../healthcare/page/patient_history/patient_history.js | 2 +- .../healthcare/page/patient_history/patient_history.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/healthcare/page/patient_history/patient_history.js b/erpnext/healthcare/page/patient_history/patient_history.js index d509ea22a2..9c44d63b96 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.js +++ b/erpnext/healthcare/page/patient_history/patient_history.js @@ -254,7 +254,7 @@ let add_to_records = function(me, data) { }; let add_date_separator = function(data) { - let date = frappe.datetime.str_to_obj(data.creation); + let date = frappe.datetime.str_to_obj(data.communication_date); let pdate = ''; let diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); diff --git a/erpnext/healthcare/page/patient_history/patient_history.py b/erpnext/healthcare/page/patient_history/patient_history.py index de8a5771d2..4cdfd64a69 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.py +++ b/erpnext/healthcare/page/patient_history/patient_history.py @@ -14,10 +14,10 @@ def get_feed(name, document_types=None, date_range=None, start=0, page_length=20 filters = get_filters(name, document_types, date_range) result = frappe.db.get_all('Patient Medical Record', - fields=['name', 'owner', 'creation', + fields=['name', 'owner', 'communication_date', 'reference_doctype', 'reference_name', 'subject'], filters=filters, - order_by='creation DESC', + order_by='communication_date DESC', limit=cint(page_length), start=cint(start) ) @@ -36,7 +36,7 @@ def get_filters(name, document_types=None, date_range=None): try: date_range = json.loads(date_range) if date_range: - filters['creation'] = ['between', [date_range[0], date_range[1]]] + filters['communication_date'] = ['between', [date_range[0], date_range[1]]] except json.decoder.JSONDecodeError: pass @@ -47,13 +47,13 @@ def get_filters(name, document_types=None, date_range=None): def get_feed_for_dt(doctype, docname): """get feed""" result = frappe.db.get_all('Patient Medical Record', - fields=['name', 'owner', 'creation', + fields=['name', 'owner', 'communication_date', 'reference_doctype', 'reference_name', 'subject'], filters={ 'reference_doctype': doctype, 'reference_name': docname }, - order_by='creation DESC' + order_by='communication_date DESC' ) return result From 19d5074c25646de3b0239eeadf56b50bb3eca667 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 30 Nov 2020 15:49:00 +0530 Subject: [PATCH 174/728] 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 b5b8c5474a14c394fae3ddb4074d7205bc3dfbbb Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 30 Nov 2020 16:09:15 +0530 Subject: [PATCH 175/728] refactor: move call to fetch doctype fields to server-side --- .../patient_history_settings.js | 47 +++++++++---------- .../patient_history_settings.py | 17 ++++++- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js index ee363462ef..bf3c5b954e 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js @@ -18,7 +18,27 @@ frappe.ui.form.on('Patient History Settings', { if (doc.selected_fields) document_fields = (JSON.parse(doc.selected_fields)).map(f => f.fieldname); - let doctype_fields = frm.events.get_doctype_fields(frm, doc.document_type, document_fields); + frm.call({ + method: 'get_doctype_fields', + doc: frm.doc, + args: { + document_type: doc.document_type, + fields: document_fields + }, + freeze: true, + callback: function(r) { + if (r.message) { + let doctype = 'Patient History Custom Document Type'; + if (standard) + doctype = 'Patient History Standard Document Type'; + + frm.events.show_field_selector_dialog(frm, doc, doctype, r.message); + } + } + }); + }, + + show_field_selector_dialog: function(frm, doc, doctype, doc_fields) { let d = new frappe.ui.Dialog({ title: __('{0} Fields', [__(doc.document_type)]), fields: [ @@ -26,7 +46,7 @@ frappe.ui.form.on('Patient History Settings', { label: __('Select Fields'), fieldtype: 'MultiCheck', fieldname: 'fields', - options: doctype_fields, + options: doc_fields, columns: 2 } ] @@ -49,9 +69,6 @@ frappe.ui.form.on('Patient History Settings', { }); } } - let doctype = 'Patient History Custom Document Type'; - if (standard) - doctype = 'Patient History Standard Document Type'; d.refresh(); frappe.model.set_value(doctype, doc.name, 'selected_fields', JSON.stringify(selected_fields)); @@ -59,26 +76,6 @@ frappe.ui.form.on('Patient History Settings', { }); d.show(); - }, - - get_doctype_fields(frm, document_type, fields) { - let multiselect_fields = []; - - frappe.model.with_doctype(document_type, () => { - // get doctype fields - frappe.get_doc('DocType', document_type).fields.forEach(field => { - if (!in_list(frappe.model.no_value_type, field.fieldtype) || - in_list(frappe.model.table_fields, field.fieldtype) && !field.hidden) { - multiselect_fields.push({ - label: field.label, - value: field.fieldname, - checked: in_list(fields, field.fieldname) - }); - } - }); - }); - - return multiselect_fields; } }); diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py index 367c34f1e8..9f18c6bbf5 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe +import json from frappe import _ from frappe.utils import cstr from frappe.model.document import Document @@ -24,6 +25,21 @@ class PatientHistorySettings(Document): frappe.throw(_('Row #{0}: Field {1} in Document Type {2} is not a Date / Datetime field.').format( entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type))) + def get_doctype_fields(self, document_type, fields): + multicheck_fields = [] + doc_fields = frappe.get_meta(document_type).fields + + for field in doc_fields: + if field.fieldtype not in frappe.model.no_value_fields or \ + field.fieldtype in frappe.model.table_fields and not field.hidden: + multicheck_fields.append({ + 'label': field.label, + 'value': field.fieldname, + 'checked': 1 if field.fieldname in fields else 0 + }) + + return multicheck_fields + def create_medical_record(doc, method=None): medical_record_required = validate_medical_record_required(doc) @@ -100,7 +116,6 @@ def get_date_field(doctype): def get_patient_history_fields(doc): - import json dt = get_patient_history_config_dt(doc.doctype) patient_history_fields = frappe.db.get_value(dt, { 'document_type': doc.doctype }, 'selected_fields') From 02d1491e39951a25b1c502fb9abf337e08a34b2c Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 30 Nov 2020 17:41:38 +0530 Subject: [PATCH 176/728] 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 f6756838ba63075e16ca5c4b3601d2eebe143d33 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 30 Nov 2020 17:51:56 +0530 Subject: [PATCH 177/728] fix: patch --- .../doctype/patient_appointment/test_patient_appointment.py | 1 + .../setup_patient_history_settings_for_standard_doctypes.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py index eeed157291..f8b7f7f2f0 100644 --- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py @@ -148,6 +148,7 @@ def create_healthcare_service_items(): item.item_name = 'Consulting Charges' item.item_group = 'Services' item.is_stock_item = 0 + item.stock_uom = 'Nos' item.save() return item.name diff --git a/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py b/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py index 3332be0561..de08aa26b3 100644 --- a/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py +++ b/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py @@ -3,7 +3,11 @@ import frappe from erpnext.healthcare.setup import setup_patient_history_settings def execute(): - if 'Healthcare' not in frappe.get_active_domains(): + if "Healthcare" not in frappe.get_active_domains(): return + frappe.reload_doc("healthcare", "doctype", "Patient History Settings") + frappe.reload_doc("healthcare", "doctype", "Patient History Standard Document Type") + frappe.reload_doc("healthcare", "doctype", "Patient History Custom Document Type") + setup_patient_history_settings() \ No newline at end of file From 188e0ecbcb11f994f16a1a1ff9a7fc5abb3bad7d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 30 Nov 2020 17:53:19 +0530 Subject: [PATCH 178/728] 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 de68f74b4c00fec82bb0ff3a7970e82616002004 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 1 Dec 2020 12:51:38 +0530 Subject: [PATCH 179/728] 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 9aaca25edb6aa740ecd64fa1caf9162a7342b1ba Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 1 Dec 2020 13:39:54 +0530 Subject: [PATCH 180/728] 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 2e27f074c3806f63f2ea4f9f70a43ba846c11346 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Tue, 1 Dec 2020 18:07:52 +0530 Subject: [PATCH 181/728] 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 ab5053ef9cbd5ce27f9253a3409bd7be24f597e2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 2 Dec 2020 12:34:40 +0530 Subject: [PATCH 182/728] fix: Accounting dimension import in PCV --- .../doctype/period_closing_voucher/period_closing_voucher.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 7dd5b01770..a74fa062b6 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -8,7 +8,7 @@ from frappe import _ from erpnext.accounts.utils import get_account_currency from erpnext.controllers.accounts_controller import AccountsController from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (get_accounting_dimensions, - get_dimension_filters) + get_dimensions) class PeriodClosingVoucher(AccountsController): def validate(self): @@ -58,7 +58,7 @@ class PeriodClosingVoucher(AccountsController): for dimension in accounting_dimensions: dimension_fields.append('t1.{0}'.format(dimension)) - dimension_filters, default_dimensions = get_dimension_filters() + dimension_filters, default_dimensions = get_dimensions() pl_accounts = self.get_pl_balances(dimension_fields) From 59820004b8b1084511664142e29e8d8dbde9e9c6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 2 Dec 2020 12:34:59 +0530 Subject: [PATCH 183/728] fix: Exception naming --- .../test_accounting_dimension_filter.py | 6 +++--- erpnext/accounts/doctype/gl_entry/gl_entry.py | 8 ++++---- erpnext/exceptions.py | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py index f67e1de404..fa700e115c 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py @@ -7,7 +7,7 @@ import frappe import unittest from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import create_dimension, disable_dimension -from erpnext.exceptions import InvalidAccountDimension, MandatoryDimension +from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError class TestAccountingDimensionFilter(unittest.TestCase): def setUp(self): @@ -20,7 +20,7 @@ class TestAccountingDimensionFilter(unittest.TestCase): si.location = 'Block 1' si.save() - self.assertRaises(InvalidAccountDimension, si.submit) + self.assertRaises(InvalidAccountDimensionError, si.submit) def test_mandatory_dimension_validation(self): si = create_sales_invoice(do_not_save=1) @@ -31,7 +31,7 @@ class TestAccountingDimensionFilter(unittest.TestCase): si.items[0].cost_center = '_Test Cost Center 2 - _TC' si.save() - self.assertRaises(MandatoryDimension, si.submit) + self.assertRaises(MandatoryAccountDimensionError, si.submit) def tearDown(self): disable_dimension_filter() diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index f586de82e3..fd6f01e345 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -11,7 +11,7 @@ from frappe.model.meta import get_field_precision from erpnext.accounts.party import validate_party_gle_currency, validate_party_frozen_disabled from erpnext.accounts.utils import get_account_currency from erpnext.accounts.utils import get_fiscal_year -from erpnext.exceptions import InvalidAccountCurrency, InvalidAccountDimension, MandatoryDimension +from erpnext.exceptions import InvalidAccountCurrency, InvalidAccountDimensionError, MandatoryAccountDimensionError from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_checks_for_pl_and_bs_accounts from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import get_dimension_filter_map from six import iteritems @@ -101,16 +101,16 @@ class GLEntry(Document): if self.account == account: if value['is_mandatory'] and not self.get(dimension): frappe.throw(_("{0} is mandatory for account {1}").format( - frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), MandatoryDimension) + frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), MandatoryAccountDimensionError) if value['allow_or_restrict'] == 'Allow': if self.get(dimension) and self.get(dimension) not in value['allowed_dimensions']: frappe.throw(_("Invalid value {0} for account {1}").format( - frappe.bold(self.get(dimension)), frappe.bold(self.account)), InvalidAccountDimension) + frappe.bold(self.get(dimension)), frappe.bold(self.account)), InvalidAccountDimensionError) else: if self.get(dimension) and self.get(dimension) in value['allowed_dimensions']: frappe.throw(_("Invalid value {0} for account {1}").format( - frappe.bold(self.get(dimension)), frappe.bold(self.account)), InvalidAccountDimension) + frappe.bold(self.get(dimension)), frappe.bold(self.account)), InvalidAccountDimensionError) def check_pl_account(self): if self.is_opening=='Yes' and \ diff --git a/erpnext/exceptions.py b/erpnext/exceptions.py index dcf3d6bad1..04291cd5bd 100644 --- a/erpnext/exceptions.py +++ b/erpnext/exceptions.py @@ -6,5 +6,5 @@ class PartyFrozen(frappe.ValidationError): pass class InvalidAccountCurrency(frappe.ValidationError): pass class InvalidCurrency(frappe.ValidationError): pass class PartyDisabled(frappe.ValidationError):pass -class InvalidAccountDimension(frappe.ValidationError): pass -class MandatoryDimension(frappe.ValidationError): pass +class InvalidAccountDimensionError(frappe.ValidationError): pass +class MandatoryAccountDimensionError(frappe.ValidationError): pass From 9abc685504cc14a949fe922d343ff42d5f0f9a2a Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Wed, 2 Dec 2020 21:13:50 +0530 Subject: [PATCH 184/728] 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 185/728] 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 2895645f86d27aa0d80564f9ac06b53fef966e62 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 3 Dec 2020 15:39:53 +0530 Subject: [PATCH 186/728] 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 187/728] 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 188/728] 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 189/728] 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 190/728] 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 191/728] 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 192/728] 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 7b2d518059f1a131a0ece9e6872c5ed8c4a93d04 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 4 Dec 2020 11:28:26 +0530 Subject: [PATCH 193/728] fix: Dimension filters in accounting reports --- erpnext/public/js/utils.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 891bbe5b59..2635d47f88 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -194,15 +194,21 @@ $.extend(erpnext.utils, { add_dimensions: function(report_name, index) { let filters = frappe.query_reports[report_name].filters; - erpnext.dimension_filters.forEach((dimension) => { - let found = filters.some(el => el.fieldname === dimension['fieldname']); + frappe.call({ + method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimensions", + callback: function(r) { + let accounting_dimensions = r.message[0]; + accounting_dimensions.forEach((dimension) => { + let found = filters.some(el => el.fieldname === dimension['fieldname']); - if (!found) { - filters.splice(index, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] + if (!found) { + filters.splice(index, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + } }); } }); From 28e86cf18312155ed020065b10152f77e8747d8f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 4 Dec 2020 00:47:46 +0530 Subject: [PATCH 194/728] 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 195/728] 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 196/728] 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 197/728] 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 243/728] 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 244/728] 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 e15ef1e19f124436d8705d633a02388a1107056f Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 10 Dec 2020 20:55:25 +0530 Subject: [PATCH 245/728] fix: corrected tests --- erpnext/projects/doctype/project/project.py | 35 +++++++----- .../projects/doctype/project/test_project.py | 57 ++++++++++++------- .../project_template/test_project_template.py | 19 ++++--- erpnext/projects/doctype/task/test_task.py | 8 +-- 4 files changed, 71 insertions(+), 48 deletions(-) diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 04a0fb6c4f..dfb54a2f77 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -54,17 +54,15 @@ class Project(Document): self.project_type = template.project_type # create tasks from template + project_tasks = [] for task in template.tasks: template_task_details = frappe.get_doc("Task", task.task) - project_task = self.create_task_from_template(template_task_details) + project_tasks.append(self.create_task_from_template(template_task_details)) - if template_task_details.depends_on: - for child_task in template_task_details.depends_on: - child_task_details = frappe.get_doc("Task",child_task.task) - self.create_task_from_template(child_task_details, project_task) + #self.dependency_mapping(template.tasks, project_tasks) - def create_task_from_template(self, task_details, project_task=None): - doc = frappe.get_doc(dict( + def create_task_from_template(self, task_details): + return frappe.get_doc(dict( doctype = 'Task', subject = task_details.subject, project = self.name, @@ -75,14 +73,21 @@ class Project(Document): task_weight = task_details.task_weight, type = task_details.type, issue = task_details.issue, - is_group = task_details.is_group - )) - if task_details.parent_task and project_task: - doc.parent_task = project_task.name - if not task_details.is_group: - doc.depends_on = task_details.depends_on - doc.insert() - return doc + is_group = task_details.is_group, + start = task_details.start, + duration = task_details.duration + )).insert() + + """ def dependency_mapping(self, template_tasks, project_tasks): + for tmp_task in template_tasks: + for prj_task in project_tasks: + if tmp_task.subject == prj_task.subject: + if tmp_task.depends_on and not prj_task.depends_on: + for child_task in tmp_task.depends_on: + child_task_detai + prj_task.depends_on = tmp_task.depends_on + """ + def is_row_updated(self, row, existing_task_data, fields): if self.get("__islocal") or not existing_task_data: return True diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py index 52f877b8b7..f9bb1b3ac4 100644 --- a/erpnext/projects/doctype/project/test_project.py +++ b/erpnext/projects/doctype/project/test_project.py @@ -20,15 +20,16 @@ class TestProject(unittest.TestCase): frappe.db.sql('delete from tabTask where project = "Test Project with Templ - no parent and dependend tasks"') frappe.delete_doc('Project', 'Test Project with Templ - no parent and dependend tasks') - if not frappe.db.exists("Task", "Test Temp Task with no parent and dependency"): - task1 = create_task(subject="Test Temp Task with no parent and dependency", is_template=1, begin=0, duration=3) + task1 = task_exists("Test Temp Task with no parent and dependency") + if not task1: + task1 = create_task(subject="Test Temp Task with no parent and dependency", is_template=1, begin=5, duration=3) template = make_project_template("Test Project Template - no parent and dependend tasks", [task1]) project = get_project("Test Project with Templ - no parent and dependend tasks", template) tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc') self.assertEqual(tasks[0].subject, 'Test Temp Task with no parent and dependency') - self.assertEqual(getdate(tasks[0].exp_end_date), add_days(nowdate() + 0 + 3)) + self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, tasks[0])) self.assertEqual(len(tasks), 1) def test_project_template_having_parent_child_tasks(self): @@ -36,28 +37,31 @@ class TestProject(unittest.TestCase): frappe.db.sql('delete from tabTask where project = "Test Project with Templ - tasks with parent-child"') frappe.delete_doc('Project', 'Test Project with Templ - tasks with parent-child') - if not frappe.db.exists("Task", "Test Temp Task parent"): + task1 = task_exists("Test Temp Task parent") + if not task1: task1 = create_task(subject="Test Temp Task parent", is_group=1, is_template=1, begin=1, duration=1) - if not frappe.db.exists("Task", "Test Temp Task child 1"): + task2 = task_exists("Test Temp Task child 1") + if not task2: task2 = create_task(subject="Test Temp Task child 1", parent_task=task1.name, is_template=1, begin=1, duration=3) - if not frappe.db.exists("Task", "Test Temp Task child 2"): + task3 = task_exists("Test Temp Task child 2") + if not task3: task3 = create_task(subject="Test Temp Task child 2", parent_task=task1.name, is_template=1, begin=2, duration=3) - template = make_project_template("Test Project Template - tasks with parent-child", [task1, task2, task3]) + template = make_project_template("Test Project Template - tasks with parent-child", [task1]) project = get_project("Test Project with Templ - tasks with parent-child", template) tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc') - print(tasks, type(tasks), len(tasks)) + print(tasks[0].duration) self.assertEqual(tasks[0].subject, 'Test Temp Task parent') - self.assertEqual(getdate(tasks[0].exp_end_date), add_days(nowdate()+ 1 + 1)) + self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, tasks[0])) self.assertEqual(tasks[1].subject, 'Test Temp Task child 1') - self.assertEqual(getdate(tasks[1].exp_end_date), add_days(nowdate()+ 1 + 3)) + self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, tasks[1])) self.assertEqual(tasks[1].parent_task, tasks[0].name) self.assertEqual(tasks[2].subject, 'Test Temp Task child 2') - self.assertEqual(getdate(tasks[2].exp_end_date), add_days(nowdate()+ 2 + 3)) + self.assertEqual(getdate(tasks[2].exp_end_date), calculate_end_date(project, tasks[2])) self.assertEqual(tasks[2].parent_task, tasks[0].name) self.assertEqual(len(tasks), 3) @@ -67,22 +71,24 @@ class TestProject(unittest.TestCase): frappe.db.sql('delete from tabTask where project = "Test Project with Templ - dependent tasks"') frappe.delete_doc('Project', 'Test Project with Templ - dependent tasks') - if not frappe.db.exists("Task", "Test Temp Task for dependency"): + task1 = task_exists("Test Temp Task for dependency") + if not task1: task1 = create_task(subject="Test Temp Task for dependency", is_template=1, begin=3, duration=1) - if not frappe.db.exists("Task", "Test Temp Task with dependency"): + task2 = task_exists("Test Temp Task with dependency") + if not task2: task2 = create_task(subject="Test Temp Task with dependency", depends_on=task1.name, is_template=1, begin=2, duration=2) - template = make_project_template("Test Project Template - tasks with parent-child", [task1, task2]) + template = make_project_template("Test Project with Templ - dependent tasks", [task2]) project = get_project("Test Project with Templ - tasks with parent-child", template) tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc') - self.assertEqual(tasks[0].subject, 'Test Temp Task for dependency') - self.assertEqual(getdate(tasks[0].exp_end_date), add_days(nowdate()+ 3 + 1)) + self.assertEqual(tasks[0].subject, 'Test Temp Task with dependency') + self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, tasks[0])) + self.assertEqual(tasks[0].depends_on, tasks[1].name) - self.assertEqual(tasks[1].subject, 'Test Temp Task with dependency') - self.assertEqual(getdate(tasks[1].exp_end_date), add_days(nowdate()+ 2 + 2)) - self.assertEqual(tasks[1].depends_on, tasks[0].name) + self.assertEqual(tasks[1].subject, 'Test Temp Task for dependency') + self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, tasks[1]) ) self.assertEqual(len(tasks), 2) @@ -93,7 +99,7 @@ def get_project(name, template): project_name = name, status = 'Open', project_template = template.name, - expected_start_date = '2019-01-01' + expected_start_date = nowdate() )).insert() return project @@ -114,4 +120,13 @@ def make_project(args): if not frappe.db.exists("Project", args.project_name): project.insert() - return project \ No newline at end of file + return project + +def task_exists(subject): + result = frappe.db.get_list("Task", filters={"subject": subject},fields=["name"]) + if not len(result): + return False + return frappe.get_doc("Task", result[0].name) + +def calculate_end_date(project, task): + return getdate(add_days(project.expected_start_date, task.start + task.duration)) \ No newline at end of file diff --git a/erpnext/projects/doctype/project_template/test_project_template.py b/erpnext/projects/doctype/project_template/test_project_template.py index 379365f999..6c6b78368e 100644 --- a/erpnext/projects/doctype/project_template/test_project_template.py +++ b/erpnext/projects/doctype/project_template/test_project_template.py @@ -17,9 +17,7 @@ def get_project_template(project_template_name="Test Project Template", project_ name = project_template_name, tasks = project_tasks or [ create_task(subject="_Test Template Task 1", is_template=1, begin=0, duration=3), - create_task(subject="_Test Template Task 2", is_template=1, begin=0, duration=2), - create_task(subject="_Test Template Task 3", is_template=1, begin=2, duration=4), - create_task(subject="_Test Template Task 4", is_template=1, begin=3, duration=2), + create_task(subject="_Test Template Task 2", is_template=1, begin=0, duration=2) ] )).insert() @@ -27,13 +25,18 @@ def get_project_template(project_template_name="Test Project Template", project_ def make_project_template(project_template_name, project_tasks=[]): if not frappe.db.exists('Project Template', project_template_name): - frappe.get_doc(dict( - doctype = 'Project Template', - name = project_template_name, - tasks = project_tasks or [ + project_tasks = project_tasks or [ create_task(subject="_Test Template Task 1", is_template=1, begin=0, duration=3), create_task(subject="_Test Template Task 2", is_template=1, begin=0, duration=2), ] - )).insert() + doc = frappe.get_doc(dict( + doctype = 'Project Template', + name = project_template_name + )) + for task in project_tasks: + doc.append("tasks",{ + "task": task.name + }) + doc.insert() return frappe.get_doc('Project Template', project_template_name) \ No newline at end of file diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py index 181a2dc316..d43d132e80 100644 --- a/erpnext/projects/doctype/task/test_task.py +++ b/erpnext/projects/doctype/task/test_task.py @@ -104,11 +104,12 @@ def create_task(subject, start=None, end=None, depends_on=None, project=None, pa task.subject = subject task.exp_start_date = start or nowdate() task.exp_end_date = end or nowdate() - task.project = project or "_Test Project" - task.is_template = is_template, + task.project = project + task.is_template = is_template task.start = begin - task.duration = duration, + task.duration = duration task.is_group = is_group + task.parent_task = parent_task if save: task.save() else: @@ -120,5 +121,4 @@ def create_task(subject, start=None, end=None, depends_on=None, project=None, pa }) if save: task.save() - return task From c9209237daa91e09832927c11ec223abbe6a99bb Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 10 Dec 2020 22:45:27 +0530 Subject: [PATCH 246/728] 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 fa72671929581c318cd0b828b07477987199a003 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Fri, 11 Dec 2020 11:16:54 +0530 Subject: [PATCH 247/728] fix: partial order for drop ship --- .../doctype/sales_order/sales_order.py | 77 +++++++++---------- 1 file changed, 35 insertions(+), 42 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 3e1c82f961..2379a304bb 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -830,52 +830,45 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=[], tar frappe.throw(_("Please set a Supplier against the Items to be considered in the Purchase Order.")) for supplier in suppliers: - po = frappe.get_list("Purchase Order", filters={"sales_order":source_name, "supplier":supplier, "docstatus": ("<", "2")}) - if len(po) == 0 or any( item.get("delivered_by_supplier") == 1 for item in selected_items): - doc = get_mapped_doc("Sales Order", source_name, { - "Sales Order": { - "doctype": "Purchase Order", - "field_no_map": [ - "address_display", - "contact_display", - "contact_mobile", - "contact_email", - "contact_person", - "taxes_and_charges", - "shipping_address" - ], - "validation": { - "docstatus": ["=", 1] - } - }, - "Sales Order Item": { - "doctype": "Purchase Order Item", - "field_map": [ - ["name", "sales_order_item"], - ["parent", "sales_order"], - ["stock_uom", "stock_uom"], - ["uom", "uom"], - ["conversion_factor", "conversion_factor"], - ["delivery_date", "schedule_date"] - ], - "field_no_map": [ - "rate", - "price_list_rate", - "item_tax_template" - ], - "postprocess": update_item, - "condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.supplier == supplier and doc.item_code in items_to_map + doc = get_mapped_doc("Sales Order", source_name, { + "Sales Order": { + "doctype": "Purchase Order", + "field_no_map": [ + "address_display", + "contact_display", + "contact_mobile", + "contact_email", + "contact_person", + "taxes_and_charges", + "shipping_address" + ], + "validation": { + "docstatus": ["=", 1] } - }, target_doc, set_missing_values) + }, + "Sales Order Item": { + "doctype": "Purchase Order Item", + "field_map": [ + ["name", "sales_order_item"], + ["parent", "sales_order"], + ["stock_uom", "stock_uom"], + ["uom", "uom"], + ["conversion_factor", "conversion_factor"], + ["delivery_date", "schedule_date"] + ], + "field_no_map": [ + "rate", + "price_list_rate", + "item_tax_template" + ], + "postprocess": update_item, + "condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.supplier == supplier and doc.item_code in items_to_map + } + }, target_doc, set_missing_values) - doc.insert() - else: - suppliers =[] - if suppliers: + doc.insert() frappe.db.commit() return doc - else: - frappe.msgprint(_("Purchase Order already created for all Sales Order items")) @frappe.whitelist() def make_purchase_order(source_name, selected_items=[], target_doc=None): From 06961a261e8560c591e2828e1733a90720fc4872 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Fri, 11 Dec 2020 11:46:43 +0530 Subject: [PATCH 248/728] fix: conflicts --- erpnext/selling/doctype/sales_order/sales_order.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 2379a304bb..5d341b746a 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -840,7 +840,8 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=[], tar "contact_email", "contact_person", "taxes_and_charges", - "shipping_address" + "shipping_address", + "terms" ], "validation": { "docstatus": ["=", 1] @@ -859,7 +860,10 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=[], tar "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 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 249/728] 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 250/728] 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 251/728] 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 252/728] 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 253/728] 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 254/728] 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 255/728] 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 256/728] 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 c47d38dc1820c3328b2e782c740fa10ad3471890 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 14 Dec 2020 11:48:04 +0530 Subject: [PATCH 257/728] chore: Stock Entry Tests and fixes - Maintain item-warehouse wise rules for Stock Entry Material Transfer - Test cases for Stock Entry with more use cases - Sider fixes --- erpnext/public/js/controllers/transaction.js | 5 +- .../doctype/putaway_rule/putaway_rule.py | 38 +-- .../doctype/putaway_rule/test_putaway_rule.py | 233 ++++++++++++++---- .../stock/doctype/stock_entry/stock_entry.js | 2 +- .../stock/doctype/stock_entry/stock_entry.py | 3 +- .../doctype/stock_entry/stock_entry_utils.py | 5 +- 6 files changed, 216 insertions(+), 70 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 54634482b0..2dabb9a647 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -2030,7 +2030,7 @@ erpnext.show_serial_batch_selector = function (frm, d, callback, on_close, show_ }); } -erpnext.apply_putaway_rule = (frm) => { +erpnext.apply_putaway_rule = (frm, purpose=null) => { if (!frm.doc.company) { frappe.throw({message: __("Please select a Company first."), title: __("Mandatory")}); } @@ -2042,7 +2042,8 @@ erpnext.apply_putaway_rule = (frm) => { doctype: frm.doctype, items: frm.doc.items, company: frm.doc.company, - sync: true + sync: true, + purpose: purpose }, callback: (result) => { if (!result.exc && result.message) { diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.py b/erpnext/stock/doctype/putaway_rule/putaway_rule.py index 4ed4dafac7..56752583fa 100644 --- a/erpnext/stock/doctype/putaway_rule/putaway_rule.py +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py @@ -64,11 +64,14 @@ def get_putaway_capacity(rule): return free_space if free_space > 0 else 0 @frappe.whitelist() -def apply_putaway_rule(doctype, items, company, sync=None): +def apply_putaway_rule(doctype, items, company, sync=None, purpose=None): """ Applies Putaway Rule on line items. - items: List of Purchase Receipt Item objects - company: Company in the Purchase Receipt + items: List of Purchase Receipt/Stock Entry Items + company: Company in the Purchase Receipt/Stock Entry + doctype: Doctype to apply rule on + purpose: Purpose of Stock Entry + sync (optional): Sync with client side only for client side calls """ if isinstance(items, string_types): items = json.loads(items) @@ -82,31 +85,36 @@ def apply_putaway_rule(doctype, items, company, sync=None): source_warehouse = item.get("s_warehouse") serial_nos = get_serial_nos(item.get("serial_no")) - item.conversion_factor = flt(item.conversion_factor) or 1 + item.conversion_factor = flt(item.conversion_factor) or 1.0 pending_qty, item_code = flt(item.qty), item.item_code pending_stock_qty = flt(item.transfer_qty) if doctype == "Stock Entry" else flt(item.stock_qty) - if not pending_qty or not item_code: - updated_table = add_row(item, pending_qty, item.warehouse, updated_table) - continue - uom_must_be_whole_number = frappe.db.get_value('UOM', item.uom, 'must_be_whole_number') + if not pending_qty or not item_code: + updated_table = add_row(item, pending_qty, source_warehouse or item.warehouse, updated_table) + continue + at_capacity, rules = get_ordered_putaway_rules(item_code, company, source_warehouse=source_warehouse) if not rules: - warehouse = item.warehouse + warehouse = source_warehouse or item.warehouse if at_capacity: - warehouse = '' # rules available, but no free space + # rules available, but no free space items_not_accomodated.append([item_code, pending_qty]) - updated_table = add_row(item, pending_qty, warehouse, updated_table) + else: + updated_table = add_row(item, pending_qty, warehouse, updated_table) continue - # maintain item wise rules, to handle if item is entered twice + # maintain item/item-warehouse wise rules, to handle if item is entered twice # in the table, due to different price, etc. - if not item_wise_rules[item_code]: - item_wise_rules[item_code] = rules + key = item_code + if doctype == "Stock Entry" and purpose == "Material Transfer" and source_warehouse: + key = (item_code, source_warehouse) - for rule in item_wise_rules[item_code]: + if not item_wise_rules[key]: + item_wise_rules[key] = rules + + for rule in item_wise_rules[key]: if pending_stock_qty > 0 and rule.free_space: stock_qty_to_allocate = flt(rule.free_space) if pending_stock_qty >= flt(rule.free_space) else pending_stock_qty qty_to_allocate = stock_qty_to_allocate / item.conversion_factor diff --git a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py index 17619e01bc..86f7dc3e08 100644 --- a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py +++ b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py @@ -4,12 +4,11 @@ from __future__ import unicode_literals import frappe import unittest -from frappe.utils import add_days, nowdate from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.get_item_details import get_conversion_factor from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry -from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order +from erpnext.stock.doctype.batch.test_batch import make_new_batch from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt class TestPutawayRule(unittest.TestCase): @@ -27,6 +26,9 @@ class TestPutawayRule(unittest.TestCase): if not frappe.db.exists("Warehouse", {"warehouse_name": "Rack 2"}): create_warehouse("Rack 2") + self.warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) + self.warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) + if not frappe.db.exists("UOM", "Bag"): new_uom = frappe.new_doc("UOM") new_uom.uom_name = "Bag" @@ -34,21 +36,18 @@ class TestPutawayRule(unittest.TestCase): def test_putaway_rules_priority(self): """Test if rule is applied by priority, irrespective of free space.""" - warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) - warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) - - rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=200, + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200, uom="Kg") - rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=300, + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=300, uom="Kg", priority=2) pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1, do_not_submit=1) self.assertEqual(len(pr.items), 2) self.assertEqual(pr.items[0].qty, 200) - self.assertEqual(pr.items[0].warehouse, warehouse_1) + self.assertEqual(pr.items[0].warehouse, self.warehouse_1) self.assertEqual(pr.items[1].qty, 100) - self.assertEqual(pr.items[1].warehouse, warehouse_2) + self.assertEqual(pr.items[1].warehouse, self.warehouse_2) pr.delete() rule_1.delete() @@ -57,26 +56,23 @@ class TestPutawayRule(unittest.TestCase): def test_putaway_rules_with_same_priority(self): """Test if rule with more free space is applied, among two rules with same priority and capacity.""" - warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) - warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) - - rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=500, + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=500, uom="Kg") - rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=500, + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=500, uom="Kg") # out of 500 kg capacity, occupy 100 kg in warehouse_1 - stock_receipt = make_stock_entry(item_code="_Rice", target=warehouse_1, qty=100, basic_rate=50) + stock_receipt = make_stock_entry(item_code="_Rice", target=self.warehouse_1, qty=100, basic_rate=50) pr = make_purchase_receipt(item_code="_Rice", qty=700, apply_putaway_rule=1, do_not_submit=1) self.assertEqual(len(pr.items), 2) self.assertEqual(pr.items[0].qty, 500) # warehouse_2 has 500 kg free space, it is given priority - self.assertEqual(pr.items[0].warehouse, warehouse_2) + self.assertEqual(pr.items[0].warehouse, self.warehouse_2) self.assertEqual(pr.items[1].qty, 200) # warehouse_1 has 400 kg free space, it is given less priority - self.assertEqual(pr.items[1].warehouse, warehouse_1) + self.assertEqual(pr.items[1].warehouse, self.warehouse_1) stock_receipt.cancel() pr.delete() @@ -85,21 +81,20 @@ class TestPutawayRule(unittest.TestCase): def test_putaway_rules_with_insufficient_capacity(self): """Test if qty exceeding capacity, is handled.""" - warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) - warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) - - rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=100, + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=100, uom="Kg") - rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=200, + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=200, uom="Kg") pr = make_purchase_receipt(item_code="_Rice", qty=350, apply_putaway_rule=1, do_not_submit=1) self.assertEqual(len(pr.items), 2) self.assertEqual(pr.items[0].qty, 200) - self.assertEqual(pr.items[0].warehouse, warehouse_2) + self.assertEqual(pr.items[0].warehouse, self.warehouse_2) self.assertEqual(pr.items[1].qty, 100) - self.assertEqual(pr.items[1].warehouse, warehouse_1) + self.assertEqual(pr.items[1].warehouse, self.warehouse_1) + # total 300 assigned, 50 unassigned + pr.delete() rule_1.delete() rule_2.delete() @@ -114,26 +109,23 @@ class TestPutawayRule(unittest.TestCase): }) item.save() - warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) - warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) - - rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=3, + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=3, uom="Bag") self.assertEqual(rule_1.stock_capacity, 3000) - rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=4, + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=4, uom="Bag") self.assertEqual(rule_2.stock_capacity, 4000) # populate 'Rack 1' with 1 Bag, making the free space 2 Bags - stock_receipt = make_stock_entry(item_code="_Rice", target=warehouse_1, qty=1000, basic_rate=50) + stock_receipt = make_stock_entry(item_code="_Rice", target=self.warehouse_1, qty=1000, basic_rate=50) pr = make_purchase_receipt(item_code="_Rice", qty=6, uom="Bag", stock_uom="Kg", conversion_factor=1000, apply_putaway_rule=1, do_not_submit=1) self.assertEqual(len(pr.items), 2) self.assertEqual(pr.items[0].qty, 4) - self.assertEqual(pr.items[0].warehouse, warehouse_2) + self.assertEqual(pr.items[0].warehouse, self.warehouse_2) self.assertEqual(pr.items[1].qty, 2) - self.assertEqual(pr.items[1].warehouse, warehouse_1) + self.assertEqual(pr.items[1].warehouse, self.warehouse_1) stock_receipt.cancel() pr.delete() @@ -152,15 +144,12 @@ class TestPutawayRule(unittest.TestCase): frappe.db.set_value("UOM", "Bag", "must_be_whole_number", 1) - warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) - warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) - # Putaway Rule in different UOM - rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=1, + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=1, uom="Bag") self.assertEqual(rule_1.stock_capacity, 1000) # Putaway Rule in Stock UOM - rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=500) + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=500) self.assertEqual(rule_2.stock_capacity, 500) # total capacity is 1500 Kg @@ -168,7 +157,7 @@ class TestPutawayRule(unittest.TestCase): conversion_factor=1000, apply_putaway_rule=1, do_not_submit=1) self.assertEqual(len(pr.items), 1) self.assertEqual(pr.items[0].qty, 1) - self.assertEqual(pr.items[0].warehouse, warehouse_1) + self.assertEqual(pr.items[0].warehouse, self.warehouse_1) # leftover space was for 500 kg (0.5 Bag) # Since Bag is a whole UOM, 1(out of 2) Bag will be unassigned @@ -177,11 +166,8 @@ class TestPutawayRule(unittest.TestCase): rule_2.delete() def test_putaway_rules_with_reoccurring_item(self): - """Test rules on same item entered multiple times.""" - warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) - warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) - - rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=200, + """Test rules on same item entered multiple times with different rate.""" + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200, uom="Kg") # total capacity is 200 Kg @@ -201,12 +187,12 @@ class TestPutawayRule(unittest.TestCase): pr.save() self.assertEqual(len(pr.items), 2) self.assertEqual(pr.items[0].qty, 100) - self.assertEqual(pr.items[0].warehouse, warehouse_1) + self.assertEqual(pr.items[0].warehouse, self.warehouse_1) self.assertEqual(pr.items[0].putaway_rule, rule_1.name) # same rule applied to second item row # with previous assignment considered self.assertEqual(pr.items[1].qty, 100) # 100 unassigned in second row from 200 - self.assertEqual(pr.items[1].warehouse, warehouse_1) + self.assertEqual(pr.items[1].warehouse, self.warehouse_1) self.assertEqual(pr.items[1].putaway_rule, rule_1.name) pr.delete() @@ -214,17 +200,14 @@ class TestPutawayRule(unittest.TestCase): def test_validate_over_receipt_in_warehouse(self): """Test if overreceipt is blocked in the presence of putaway rules.""" - warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) - warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"}) - - rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=200, + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200, uom="Kg") pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1, do_not_submit=1) self.assertEqual(len(pr.items), 1) self.assertEqual(pr.items[0].qty, 200) # 100 is unassigned fro 300 Kg - self.assertEqual(pr.items[0].warehouse, warehouse_1) + self.assertEqual(pr.items[0].warehouse, self.warehouse_1) self.assertEqual(pr.items[0].putaway_rule, rule_1.name) # force overreceipt and disable apply putaway rule in PR @@ -236,6 +219,156 @@ class TestPutawayRule(unittest.TestCase): pr.delete() rule_1.delete() + def test_putaway_rule_on_stock_entry_material_transfer(self): + """Test if source warehouse is considered while applying rules.""" + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200, + uom="Kg") # higher priority + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=100, + uom="Kg", priority=2) + + stock_entry = make_stock_entry(item_code="_Rice", source=self.warehouse_1, qty=200, + target="_Test Warehouse - _TC", purpose="Material Transfer", + apply_putaway_rule=1, do_not_submit=1) + + stock_entry_item = stock_entry.get("items")[0] + + # since source warehouse is Rack 1, rule 1 (for Rack 1) will be avoided + # even though it has more free space and higher priority + self.assertEqual(stock_entry_item.t_warehouse, self.warehouse_2) + self.assertEqual(stock_entry_item.qty, 100) # unassigned 100 out of 200 Kg + self.assertEqual(stock_entry_item.putaway_rule, rule_2.name) + + stock_entry.delete() + rule_1.delete() + rule_2.delete() + + def test_putaway_rule_on_stock_entry_material_transfer_reoccuring_item(self): + """Test if reoccuring item is correctly considered.""" + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=300, + uom="Kg") + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=600, + uom="Kg", priority=2) + + # create SE with first row having source warehouse as Rack 2 + stock_entry = make_stock_entry(item_code="_Rice", source=self.warehouse_2, qty=200, + target="_Test Warehouse - _TC", purpose="Material Transfer", + apply_putaway_rule=1, do_not_submit=1) + + # Add rows with source warehouse as Rack 1 + stock_entry.extend("items", [ + { + "item_code": "_Rice", + "s_warehouse": self.warehouse_1, + "t_warehouse": "_Test Warehouse - _TC", + "qty": 100, + "basic_rate": 50, + "conversion_factor": 1.0, + "transfer_qty": 100 + }, + { + "item_code": "_Rice", + "s_warehouse": self.warehouse_1, + "t_warehouse": "_Test Warehouse - _TC", + "qty": 200, + "basic_rate": 60, + "conversion_factor": 1.0, + "transfer_qty": 200 + } + ]) + + stock_entry.save() + + # since source warehouse was Rack 2, exclude rule_2 + self.assertEqual(stock_entry.items[0].t_warehouse, self.warehouse_1) + self.assertEqual(stock_entry.items[0].qty, 200) + self.assertEqual(stock_entry.items[0].putaway_rule, rule_1.name) + + # since source warehouse was Rack 1, exclude rule_1 even though it has + # higher priority + self.assertEqual(stock_entry.items[1].t_warehouse, self.warehouse_2) + self.assertEqual(stock_entry.items[1].qty, 100) + self.assertEqual(stock_entry.items[1].putaway_rule, rule_2.name) + + self.assertEqual(stock_entry.items[2].t_warehouse, self.warehouse_2) + self.assertEqual(stock_entry.items[2].qty, 200) + self.assertEqual(stock_entry.items[2].putaway_rule, rule_2.name) + + stock_entry.delete() + rule_1.delete() + rule_2.delete() + + def test_putaway_rule_on_stock_entry_material_transfer_batch_serial_item(self): + """Test if batch and serial items are split correctly.""" + if not frappe.db.exists("Item", "Water Bottle"): + make_item("Water Bottle", { + "is_stock_item": 1, + "has_batch_no" : 1, + "create_new_batch": 1, + "has_serial_no": 1, + "serial_no_series": "BOTTL-.####", + "stock_uom": "Nos" + }) + + rule_1 = create_putaway_rule(item_code="Water Bottle", warehouse=self.warehouse_1, capacity=3, + uom="Nos") + rule_2 = create_putaway_rule(item_code="Water Bottle", warehouse=self.warehouse_2, capacity=2, + uom="Nos") + + make_new_batch(batch_id="BOTTL-BATCH-1", item_code="Water Bottle") + + pr = make_purchase_receipt(item_code="Water Bottle", qty=5, do_not_submit=1) + pr.items[0].batch_no = "BOTTL-BATCH-1" + pr.save() + pr.submit() + + serial_nos = frappe.get_list("Serial No", filters={"purchase_document_no": pr.name, "status": "Active"}) + serial_nos = [d.name for d in serial_nos] + + stock_entry = make_stock_entry(item_code="Water Bottle", source="_Test Warehouse - _TC", qty=5, + target="Finished Goods - _TC", purpose="Material Transfer", + apply_putaway_rule=1, do_not_save=1) + stock_entry.items[0].batch_no = "BOTTL-BATCH-1" + stock_entry.items[0].serial_no = "\n".join(serial_nos) + stock_entry.save() + + self.assertEqual(stock_entry.items[0].t_warehouse, self.warehouse_1) + self.assertEqual(stock_entry.items[0].qty, 3) + self.assertEqual(stock_entry.items[0].putaway_rule, rule_1.name) + self.assertEqual(stock_entry.items[0].serial_no, "\n".join(serial_nos[:3])) + self.assertEqual(stock_entry.items[0].batch_no, "BOTTL-BATCH-1") + + self.assertEqual(stock_entry.items[1].t_warehouse, self.warehouse_2) + self.assertEqual(stock_entry.items[1].qty, 2) + self.assertEqual(stock_entry.items[1].putaway_rule, rule_2.name) + self.assertEqual(stock_entry.items[1].serial_no, "\n".join(serial_nos[3:])) + self.assertEqual(stock_entry.items[1].batch_no, "BOTTL-BATCH-1") + + stock_entry.delete() + pr.cancel() + rule_1.delete() + rule_2.delete() + + def test_putaway_rule_on_stock_entry_material_receipt(self): + """Test if rules are applied in Stock Entry of type Receipt.""" + rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200, + uom="Kg") # more capacity + rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=100, + uom="Kg") + + stock_entry = make_stock_entry(item_code="_Rice", qty=100, + target="_Test Warehouse - _TC", purpose="Material Receipt", + apply_putaway_rule=1, do_not_submit=1) + + stock_entry_item = stock_entry.get("items")[0] + + self.assertEqual(stock_entry_item.t_warehouse, self.warehouse_1) + self.assertEqual(stock_entry_item.qty, 100) + self.assertEqual(stock_entry_item.putaway_rule, rule_1.name) + + stock_entry.delete() + rule_1.delete() + rule_2.delete() + def create_putaway_rule(**args): args = frappe._dict(args) putaway = frappe.new_doc("Putaway Rule") diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index fd920a585a..5d9ea8daee 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -574,7 +574,7 @@ frappe.ui.form.on('Stock Entry', { }, apply_putaway_rule: function (frm) { - if (frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(frm); + if (frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(frm, frm.doc.purpose); } }); diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index aed69e11dd..e37e1223bc 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -47,7 +47,8 @@ class StockEntry(StockController): apply_rule = self.apply_putaway_rule and (self.purpose in ["Material Transfer", "Material Receipt"]) if self.get("items") and apply_rule: - apply_putaway_rule(self.doctype, self.get("items"), self.company) + apply_putaway_rule(self.doctype, self.get("items"), self.company, + purpose=self.purpose) def validate(self): self.pro_doc = frappe._dict() diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py index b78c6be983..b12a8547fe 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py @@ -53,6 +53,8 @@ def make_stock_entry(**args): args.target = args.to_warehouse if args.item_code: args.item = args.item_code + if args.apply_putaway_rule: + s.apply_putaway_rule = args.apply_putaway_rule if isinstance(args.qty, string_types): if '.' in args.qty: @@ -118,7 +120,8 @@ def make_stock_entry(**args): "t_warehouse": args.target, "qty": args.qty, "basic_rate": args.rate or args.basic_rate, - "conversion_factor": 1.0, + "conversion_factor": args.conversion_factor or 1.0, + "transfer_qty": flt(args.qty) * (flt(args.conversion_factor) or 1.0), "serial_no": args.serial_no, 'batch_no': args.batch_no, 'cost_center': args.cost_center, From f9751f1f95e9f69701da999bcb342c6b620bcf95 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Mon, 14 Dec 2020 16:20:02 +0530 Subject: [PATCH 258/728] feat: project template having dependent tasks --- erpnext/projects/doctype/project/project.py | 33 +- .../project_template/project_template.py | 23 +- erpnext/projects/doctype/task/task.py | 451 +++++++++--------- 3 files changed, 282 insertions(+), 225 deletions(-) diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index dfb54a2f77..2d3339773a 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -55,11 +55,13 @@ class Project(Document): # create tasks from template project_tasks = [] + tmp_task_details = [] for task in template.tasks: template_task_details = frappe.get_doc("Task", task.task) + tmp_task_details.append(template_task_details) project_tasks.append(self.create_task_from_template(template_task_details)) - #self.dependency_mapping(template.tasks, project_tasks) + self.dependency_mapping(tmp_task_details, project_tasks) def create_task_from_template(self, task_details): return frappe.get_doc(dict( @@ -78,16 +80,33 @@ class Project(Document): duration = task_details.duration )).insert() - """ def dependency_mapping(self, template_tasks, project_tasks): + def dependency_mapping(self, template_tasks, project_tasks): for tmp_task in template_tasks: for prj_task in project_tasks: if tmp_task.subject == prj_task.subject: - if tmp_task.depends_on and not prj_task.depends_on: - for child_task in tmp_task.depends_on: - child_task_detai - prj_task.depends_on = tmp_task.depends_on - """ + self.check_depends_on_value(tmp_task, prj_task, project_tasks) + self.check_for_parent_tasks(tmp_task, prj_task, project_tasks) + def check_depends_on_value(self, tmp_task, prj_task, project_tasks): + if tmp_task.depends_on and not prj_task.depends_on: + for child_task in tmp_task.depends_on: + child_task_subject = frappe.db.get_value("Task", child_task.task, "subject") + corresponding_prj_task = list(filter(lambda x: x.subject == child_task_subject, project_tasks)) + if len(corresponding_prj_task): + prj_task.append("depends_on",{ + "task": corresponding_prj_task[0].name + }) + prj_task.save() + + def check_for_parent_tasks(self, tmp_task, prj_task, project_tasks): + if tmp_task.parent_task and not prj_task.parent_task: + parent_task_subject = frappe.db.get_value("Task", tmp_task.parent_task, "subject") + corresponding_prj_task = list(filter(lambda x: x.subject == parent_task_subject, project_tasks)) + if len(corresponding_prj_task): + prj_task.parent_task = corresponding_prj_task[0].name + print(prj_task.name, prj_task.parent_task, corresponding_prj_task[0].name) + prj_task.save() + print(prj_task.name, corresponding_prj_task[0].name) def is_row_updated(self, row, existing_task_data, fields): if self.get("__islocal") or not existing_task_data: return True diff --git a/erpnext/projects/doctype/project_template/project_template.py b/erpnext/projects/doctype/project_template/project_template.py index ac78135fc4..1beebf7a25 100644 --- a/erpnext/projects/doctype/project_template/project_template.py +++ b/erpnext/projects/doctype/project_template/project_template.py @@ -3,8 +3,27 @@ # For license information, please see license.txt from __future__ import unicode_literals -# import frappe +import frappe from frappe.model.document import Document +from frappe import _ class ProjectTemplate(Document): - pass + + def validate(self): + self.validate_dependencies() + + def validate_dependencies(self): + for task in self.tasks: + task_details = frappe.get_doc("Task", task.task) + if task_details.depends_on: + for dependency_task in task_details.depends_on: + if not self.check_dependent_task_presence(dependency_task.task): + task_details_format = """{0}""".format(task_details.name) + dependency_task_format = """{0}""".format(dependency_task.task) + frappe.throw(_("Task {0} depends on Task {1}. Please add Task {1} to the Tasks list.").format(frappe.bold(task_details_format), frappe.bold(dependency_task_format))) + + def check_dependent_task_presence(self, task): + for task_details in self.tasks: + if task_details.task == task: + return True + return False diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index fb84094ffe..072a848f26 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -17,291 +17,310 @@ class CircularReferenceError(frappe.ValidationError): pass class EndDateCannotBeGreaterThanProjectEndDateError(frappe.ValidationError): pass class Task(NestedSet): - nsm_parent_field = 'parent_task' + nsm_parent_field = 'parent_task' - def get_feed(self): - return '{0}: {1}'.format(_(self.status), self.subject) + def get_feed(self): + return '{0}: {1}'.format(_(self.status), self.subject) - def get_customer_details(self): - cust = frappe.db.sql("select customer_name from `tabCustomer` where name=%s", self.customer) - if cust: - ret = {'customer_name': cust and cust[0][0] or ''} - return ret + def get_customer_details(self): + cust = frappe.db.sql("select customer_name from `tabCustomer` where name=%s", self.customer) + if cust: + ret = {'customer_name': cust and cust[0][0] or ''} + return ret - def validate(self): - self.validate_dates() - self.validate_parent_project_dates() - self.validate_progress() - self.validate_status() - self.update_depends_on() + def validate(self): + self.validate_dates() + self.validate_parent_project_dates() + self.validate_progress() + self.validate_status() + self.update_depends_on() + self.validate_dependencies_for_template_task() - def validate_dates(self): - if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date): - frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Expected Start Date"), \ - frappe.bold("Expected End Date"))) + def validate_dates(self): + if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date): + frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Expected Start Date"), \ + frappe.bold("Expected End Date"))) - if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date): - frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \ - frappe.bold("Actual End Date"))) + if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date): + frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \ + frappe.bold("Actual End Date"))) - def validate_parent_project_dates(self): - if not self.project or frappe.flags.in_test: - return + def validate_parent_project_dates(self): + if not self.project or frappe.flags.in_test: + return - expected_end_date = frappe.db.get_value("Project", self.project, "expected_end_date") + expected_end_date = frappe.db.get_value("Project", self.project, "expected_end_date") - if expected_end_date: - validate_project_dates(getdate(expected_end_date), self, "exp_start_date", "exp_end_date", "Expected") - validate_project_dates(getdate(expected_end_date), self, "act_start_date", "act_end_date", "Actual") + if expected_end_date: + validate_project_dates(getdate(expected_end_date), self, "exp_start_date", "exp_end_date", "Expected") + validate_project_dates(getdate(expected_end_date), self, "act_start_date", "act_end_date", "Actual") - def validate_status(self): - if self.status!=self.get_db_value("status") and self.status == "Completed": - for d in self.depends_on: - if frappe.db.get_value("Task", d.task, "status") not in ("Completed", "Cancelled"): - frappe.throw(_("Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.").format(frappe.bold(self.name), frappe.bold(d.task))) + def validate_status(self): + if self.status!=self.get_db_value("status") and self.status == "Completed": + for d in self.depends_on: + if frappe.db.get_value("Task", d.task, "status") not in ("Completed", "Cancelled"): + frappe.throw(_("Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.").format(frappe.bold(self.name), frappe.bold(d.task))) - close_all_assignments(self.doctype, self.name) + close_all_assignments(self.doctype, self.name) - def validate_progress(self): - if flt(self.progress or 0) > 100: - frappe.throw(_("Progress % for a task cannot be more than 100.")) + def validate_progress(self): + if flt(self.progress or 0) > 100: + frappe.throw(_("Progress % for a task cannot be more than 100.")) - if flt(self.progress) == 100: - self.status = 'Completed' + if flt(self.progress) == 100: + self.status = 'Completed' - if self.status == 'Completed': - self.progress = 100 + if self.status == 'Completed': + self.progress = 100 - def update_depends_on(self): - depends_on_tasks = self.depends_on_tasks or "" - for d in self.depends_on: - if d.task and not d.task in depends_on_tasks: - depends_on_tasks += d.task + "," - self.depends_on_tasks = depends_on_tasks + def validate_dependencies_for_template_task(self): + if self.is_template: + self.validate_parent_template_task() + self.validate_depends_on_tasks() + + def validate_parent_template_task(self): + if self.parent_task: + if not frappe.db.get_value("Task", self.parent_task, "is_template"): + parent_task_format = """{0}""".format(self.parent_task) + frappe.throw(_("Parent Task {0} is not a Template Task").format(parent_task_format)) + + def validate_depends_on_tasks(self): + if self.depends_on: + for task in self.depends_on: + if not frappe.db.get_value("Task", task.task, "is_template"): + dependent_task_format = """{0}""".format(task.task) + frappe.throw(_("Dependent Task {0} is not a Template Task").format(dependent_task_format)) - def update_nsm_model(self): - frappe.utils.nestedset.update_nsm(self) + def update_depends_on(self): + depends_on_tasks = self.depends_on_tasks or "" + for d in self.depends_on: + if d.task and not d.task in depends_on_tasks: + depends_on_tasks += d.task + "," + self.depends_on_tasks = depends_on_tasks - def on_update(self): - self.update_nsm_model() - self.check_recursion() - self.reschedule_dependent_tasks() - self.update_project() - self.unassign_todo() - self.populate_depends_on() + def update_nsm_model(self): + frappe.utils.nestedset.update_nsm(self) - def unassign_todo(self): - if self.status == "Completed": - close_all_assignments(self.doctype, self.name) - if self.status == "Cancelled": - clear(self.doctype, self.name) + def on_update(self): + self.update_nsm_model() + self.check_recursion() + self.reschedule_dependent_tasks() + self.update_project() + self.unassign_todo() + self.populate_depends_on() - def update_total_expense_claim(self): - self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim` - where project = %s and task = %s and docstatus=1""",(self.project, self.name))[0][0] + def unassign_todo(self): + if self.status == "Completed": + close_all_assignments(self.doctype, self.name) + if self.status == "Cancelled": + clear(self.doctype, self.name) - def update_time_and_costing(self): - tl = frappe.db.sql("""select min(from_time) as start_date, max(to_time) as end_date, - sum(billing_amount) as total_billing_amount, sum(costing_amount) as total_costing_amount, - sum(hours) as time from `tabTimesheet Detail` where task = %s and docstatus=1""" - ,self.name, as_dict=1)[0] - if self.status == "Open": - self.status = "Working" - self.total_costing_amount= tl.total_costing_amount - self.total_billing_amount= tl.total_billing_amount - self.actual_time= tl.time - self.act_start_date= tl.start_date - self.act_end_date= tl.end_date + def update_total_expense_claim(self): + self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim` + where project = %s and task = %s and docstatus=1""",(self.project, self.name))[0][0] - def update_project(self): - if self.project and not self.flags.from_project: - frappe.get_cached_doc("Project", self.project).update_project() + def update_time_and_costing(self): + tl = frappe.db.sql("""select min(from_time) as start_date, max(to_time) as end_date, + sum(billing_amount) as total_billing_amount, sum(costing_amount) as total_costing_amount, + sum(hours) as time from `tabTimesheet Detail` where task = %s and docstatus=1""" + ,self.name, as_dict=1)[0] + if self.status == "Open": + self.status = "Working" + self.total_costing_amount= tl.total_costing_amount + self.total_billing_amount= tl.total_billing_amount + self.actual_time= tl.time + self.act_start_date= tl.start_date + self.act_end_date= tl.end_date - def check_recursion(self): - if self.flags.ignore_recursion_check: return - check_list = [['task', 'parent'], ['parent', 'task']] - for d in check_list: - task_list, count = [self.name], 0 - while (len(task_list) > count ): - tasks = frappe.db.sql(" select %s from `tabTask Depends On` where %s = %s " % - (d[0], d[1], '%s'), cstr(task_list[count])) - count = count + 1 - for b in tasks: - if b[0] == self.name: - frappe.throw(_("Circular Reference Error"), CircularReferenceError) - if b[0]: - task_list.append(b[0]) + def update_project(self): + if self.project and not self.flags.from_project: + frappe.get_cached_doc("Project", self.project).update_project() - if count == 15: - break + def check_recursion(self): + if self.flags.ignore_recursion_check: return + check_list = [['task', 'parent'], ['parent', 'task']] + for d in check_list: + task_list, count = [self.name], 0 + while (len(task_list) > count ): + tasks = frappe.db.sql(" select %s from `tabTask Depends On` where %s = %s " % + (d[0], d[1], '%s'), cstr(task_list[count])) + count = count + 1 + for b in tasks: + if b[0] == self.name: + frappe.throw(_("Circular Reference Error"), CircularReferenceError) + if b[0]: + task_list.append(b[0]) - def reschedule_dependent_tasks(self): - end_date = self.exp_end_date or self.act_end_date - if end_date: - for task_name in frappe.db.sql(""" - select name from `tabTask` as parent - where parent.project = %(project)s - and parent.name in ( - select parent from `tabTask Depends On` as child - where child.task = %(task)s and child.project = %(project)s) - """, {'project': self.project, 'task':self.name }, as_dict=1): - task = frappe.get_doc("Task", task_name.name) - if task.exp_start_date and task.exp_end_date and task.exp_start_date < getdate(end_date) and task.status == "Open": - task_duration = date_diff(task.exp_end_date, task.exp_start_date) - task.exp_start_date = add_days(end_date, 1) - task.exp_end_date = add_days(task.exp_start_date, task_duration) - task.flags.ignore_recursion_check = True - task.save() + if count == 15: + break - def has_webform_permission(self): - project_user = frappe.db.get_value("Project User", {"parent": self.project, "user":frappe.session.user} , "user") - if project_user: - return True + def reschedule_dependent_tasks(self): + end_date = self.exp_end_date or self.act_end_date + if end_date: + for task_name in frappe.db.sql(""" + select name from `tabTask` as parent + where parent.project = %(project)s + and parent.name in ( + select parent from `tabTask Depends On` as child + where child.task = %(task)s and child.project = %(project)s) + """, {'project': self.project, 'task':self.name }, as_dict=1): + task = frappe.get_doc("Task", task_name.name) + if task.exp_start_date and task.exp_end_date and task.exp_start_date < getdate(end_date) and task.status == "Open": + task_duration = date_diff(task.exp_end_date, task.exp_start_date) + task.exp_start_date = add_days(end_date, 1) + task.exp_end_date = add_days(task.exp_start_date, task_duration) + task.flags.ignore_recursion_check = True + task.save() - def populate_depends_on(self): - if self.parent_task: - parent = frappe.get_doc('Task', self.parent_task) - if not self.name in [row.task for row in parent.depends_on]: - parent.append("depends_on", { - "doctype": "Task Depends On", - "task": self.name, - "subject": self.subject - }) - parent.save() + def has_webform_permission(self): + project_user = frappe.db.get_value("Project User", {"parent": self.project, "user":frappe.session.user} , "user") + if project_user: + return True - def on_trash(self): - if check_if_child_exists(self.name): - throw(_("Child Task exists for this Task. You can not delete this Task.")) + def populate_depends_on(self): + if self.parent_task: + parent = frappe.get_doc('Task', self.parent_task) + if not self.name in [row.task for row in parent.depends_on]: + parent.append("depends_on", { + "doctype": "Task Depends On", + "task": self.name, + "subject": self.subject + }) + parent.save() - self.update_nsm_model() + def on_trash(self): + if check_if_child_exists(self.name): + throw(_("Child Task exists for this Task. You can not delete this Task.")) - def after_delete(self): - self.update_project() + self.update_nsm_model() - def update_status(self): - if self.status not in ('Cancelled', 'Completed') and self.exp_end_date: - from datetime import datetime - if self.exp_end_date < datetime.now().date(): - self.db_set('status', 'Overdue', update_modified=False) - self.update_project() + def after_delete(self): + self.update_project() + + def update_status(self): + if self.status not in ('Cancelled', 'Completed') and self.exp_end_date: + from datetime import datetime + if self.exp_end_date < datetime.now().date(): + self.db_set('status', 'Overdue', update_modified=False) + self.update_project() @frappe.whitelist() def check_if_child_exists(name): - child_tasks = frappe.get_all("Task", filters={"parent_task": name}) - child_tasks = [get_link_to_form("Task", task.name) for task in child_tasks] - return child_tasks + child_tasks = frappe.get_all("Task", filters={"parent_task": name}) + child_tasks = [get_link_to_form("Task", task.name) for task in child_tasks] + return child_tasks @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_project(doctype, txt, searchfield, start, page_len, filters): - from erpnext.controllers.queries import get_match_cond - return frappe.db.sql(""" select name from `tabProject` - where %(key)s like %(txt)s - %(mcond)s - order by name - limit %(start)s, %(page_len)s""" % { - 'key': searchfield, - 'txt': frappe.db.escape('%' + txt + '%'), - 'mcond':get_match_cond(doctype), - 'start': start, - 'page_len': page_len - }) + from erpnext.controllers.queries import get_match_cond + return frappe.db.sql(""" select name from `tabProject` + where %(key)s like %(txt)s + %(mcond)s + order by name + limit %(start)s, %(page_len)s""" % { + 'key': searchfield, + 'txt': frappe.db.escape('%' + txt + '%'), + 'mcond':get_match_cond(doctype), + 'start': start, + 'page_len': page_len + }) @frappe.whitelist() def set_multiple_status(names, status): - names = json.loads(names) - for name in names: - task = frappe.get_doc("Task", name) - task.status = status - task.save() + names = json.loads(names) + for name in names: + task = frappe.get_doc("Task", name) + task.status = status + task.save() def set_tasks_as_overdue(): - tasks = frappe.get_all("Task", filters={"status": ["not in", ["Cancelled", "Completed"]]}, fields=["name", "status", "review_date"]) - for task in tasks: - if task.status == "Pending Review": - if getdate(task.review_date) > getdate(today()): - continue - frappe.get_doc("Task", task.name).update_status() + tasks = frappe.get_all("Task", filters={"status": ["not in", ["Cancelled", "Completed"]]}, fields=["name", "status", "review_date"]) + for task in tasks: + if task.status == "Pending Review": + if getdate(task.review_date) > getdate(today()): + continue + frappe.get_doc("Task", task.name).update_status() @frappe.whitelist() def make_timesheet(source_name, target_doc=None, ignore_permissions=False): - def set_missing_values(source, target): - target.append("time_logs", { - "hours": source.actual_time, - "completed": source.status == "Completed", - "project": source.project, - "task": source.name - }) + def set_missing_values(source, target): + target.append("time_logs", { + "hours": source.actual_time, + "completed": source.status == "Completed", + "project": source.project, + "task": source.name + }) - doclist = get_mapped_doc("Task", source_name, { - "Task": { - "doctype": "Timesheet" - } - }, target_doc, postprocess=set_missing_values, ignore_permissions=ignore_permissions) + doclist = get_mapped_doc("Task", source_name, { + "Task": { + "doctype": "Timesheet" + } + }, target_doc, postprocess=set_missing_values, ignore_permissions=ignore_permissions) - return doclist + return doclist @frappe.whitelist() def get_children(doctype, parent, task=None, project=None, is_root=False): - filters = [['docstatus', '<', '2']] + filters = [['docstatus', '<', '2']] - if task: - filters.append(['parent_task', '=', task]) - elif parent and not is_root: - # via expand child - filters.append(['parent_task', '=', parent]) - else: - filters.append(['ifnull(`parent_task`, "")', '=', '']) + if task: + filters.append(['parent_task', '=', task]) + elif parent and not is_root: + # via expand child + filters.append(['parent_task', '=', parent]) + else: + filters.append(['ifnull(`parent_task`, "")', '=', '']) - if project: - filters.append(['project', '=', project]) + if project: + filters.append(['project', '=', project]) - tasks = frappe.get_list(doctype, fields=[ - 'name as value', - 'subject as title', - 'is_group as expandable' - ], filters=filters, order_by='name') + tasks = frappe.get_list(doctype, fields=[ + 'name as value', + 'subject as title', + 'is_group as expandable' + ], filters=filters, order_by='name') - # return tasks - return tasks + # return tasks + return tasks @frappe.whitelist() def add_node(): - from frappe.desk.treeview import make_tree_args - args = frappe.form_dict - args.update({ - "name_field": "subject" - }) - args = make_tree_args(**args) + from frappe.desk.treeview import make_tree_args + args = frappe.form_dict + args.update({ + "name_field": "subject" + }) + args = make_tree_args(**args) - if args.parent_task == 'All Tasks' or args.parent_task == args.project: - args.parent_task = None + if args.parent_task == 'All Tasks' or args.parent_task == args.project: + args.parent_task = None - frappe.get_doc(args).insert() + frappe.get_doc(args).insert() @frappe.whitelist() def add_multiple_tasks(data, parent): - data = json.loads(data) - new_doc = {'doctype': 'Task', 'parent_task': parent if parent!="All Tasks" else ""} - new_doc['project'] = frappe.db.get_value('Task', {"name": parent}, 'project') or "" + data = json.loads(data) + new_doc = {'doctype': 'Task', 'parent_task': parent if parent!="All Tasks" else ""} + new_doc['project'] = frappe.db.get_value('Task', {"name": parent}, 'project') or "" - for d in data: - if not d.get("subject"): continue - new_doc['subject'] = d.get("subject") - new_task = frappe.get_doc(new_doc) - new_task.insert() + for d in data: + if not d.get("subject"): continue + new_doc['subject'] = d.get("subject") + new_task = frappe.get_doc(new_doc) + new_task.insert() def on_doctype_update(): - frappe.db.add_index("Task", ["lft", "rgt"]) + frappe.db.add_index("Task", ["lft", "rgt"]) def validate_project_dates(project_end_date, task, task_start, task_end, actual_or_expected_date): - if task.get(task_start) and date_diff(project_end_date, getdate(task.get(task_start))) < 0: - frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date)) + if task.get(task_start) and date_diff(project_end_date, getdate(task.get(task_start))) < 0: + frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date)) - if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0: - frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date)) + if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0: + frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date)) From 46f3c65b003442ecfcdbcdc1d13422f872ea88bf Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 14 Dec 2020 23:09:19 +0530 Subject: [PATCH 259/728] feat: Multi currency in landed cost voucher --- .../landed_cost_taxes_and_charges.json | 32 +++++++++- .../landed_cost_voucher.js | 58 +++++++++++++++++-- .../landed_cost_voucher.json | 26 ++++++++- .../landed_cost_voucher.py | 31 ++++++---- 4 files changed, 129 insertions(+), 18 deletions(-) diff --git a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json index 0cc243d4cb..ae8747fc83 100644 --- a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json +++ b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json @@ -1,12 +1,16 @@ { + "actions": [], "creation": "2014-07-11 11:51:00.453717", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ "expense_account", + "account_currency", + "exchange_rate", "description", "col_break3", + "base_amount", "amount" ], "fields": [ @@ -27,7 +31,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", - "options": "Company:company:default_currency", + "options": "account_currency", "reqd": 1 }, { @@ -37,10 +41,34 @@ "label": "Expense Account", "options": "Account", "reqd": 1 + }, + { + "fieldname": "account_currency", + "fieldtype": "Link", + "label": "Account Currency", + "options": "Currency", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "exchange_rate", + "fieldtype": "Float", + "label": "Exchange Rate", + "precision": "9" + }, + { + "fieldname": "base_amount", + "fieldtype": "Currency", + "label": "Base Amount", + "options": "Company:company:default_currency", + "read_only": 1, + "reqd": 1 } ], + "index_web_pages_for_search": 1, "istable": 1, - "modified": "2019-09-30 18:28:32.070655", + "links": [], + "modified": "2020-12-13 21:04:01.769989", "modified_by": "Administrator", "module": "Stock", "name": "Landed Cost Taxes and Charges", diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js index 5de1352518..a5ac60c4f5 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js @@ -32,9 +32,8 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ this.frm.set_query("expense_account", "taxes", function() { return { - query: "erpnext.controllers.queries.tax_account_query", filters: { - "account_type": ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"], + "account_type": ['in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"]], "company": me.frm.doc.company } }; @@ -97,7 +96,7 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ set_total_taxes_and_charges: function() { var total_taxes_and_charges = 0.0; $.each(this.frm.doc.taxes || [], function(i, d) { - total_taxes_and_charges += flt(d.amount) + total_taxes_and_charges += flt(d.base_amount) }); cur_frm.set_value("total_taxes_and_charges", total_taxes_and_charges); }, @@ -134,7 +133,58 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ items_remove: () => { this.trigger('set_applicable_charges_for_item'); } - }); cur_frm.script_manager.make(erpnext.stock.LandedCostVoucher); + +frappe.ui.form.on('Landed Cost Voucher', { + set_account_currency: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + if (row.expense_account) { + frappe.db.get_value('Account', row.expense_account, 'account_currency', function(value) { + frappe.model.set_value(cdt, cdn, "account_currency", value.account_currency); + frm.events.set_exchange_rate(frm, cdt, cdn); + }); + } + }, + + set_exchange_rate: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; + + if(row.account_currency == company_currency) { + row.exchange_rate = 1; + } else if (!row.exchange_rate || row.exchange_rate == 1) { + frappe.call({ + method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate", + args: { + posting_date: frm.doc.posting_date, + account: row.expense_account, + account_currency: row.account_currency, + company: frm.doc.company + }, + callback: function(r) { + if(r.message) { + frappe.model.set_value(cdt, cdn, "exchange_rate", r.message); + } + } + }) + } + }, + + set_base_amount: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + frappe.model.set_value(cdt, cdn, "base_amount", + flt(flt(row.amount)*row.exchange_rate, precision("base_amount", row))); + } +}) + +frappe.ui.form.on('Landed Cost Taxes and Charges', { + expense_account: function(frm, cdt, cdn) { + frm.events.set_account_currency(frm, cdt, cdn); + }, + + amount: function(frm, cdt, cdn) { + frm.events.set_base_amount(frm, cdt, cdn); + } +}); diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json index 01492807de..6a7994ba9e 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "naming_series:", "creation": "2014-07-11 11:33:42.547339", "doctype": "DocType", @@ -7,6 +8,9 @@ "field_order": [ "naming_series", "company", + "column_break_2", + "posting_date", + "section_break_5", "purchase_receipts", "purchase_receipt_items", "get_items_from_purchase_receipts", @@ -86,7 +90,7 @@ { "fieldname": "total_taxes_and_charges", "fieldtype": "Currency", - "label": "Total Taxes and Charges", + "label": "Total Taxes and Charges (Company Currency)", "options": "Company:company:default_currency", "read_only": 1, "reqd": 1 @@ -119,11 +123,29 @@ "fieldname": "landed_cost_help", "fieldtype": "HTML", "label": "Landed Cost Help" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "reqd": 1 + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "hide_border": 1 } ], "icon": "icon-usd", + "index_web_pages_for_search": 1, "is_submittable": 1, - "modified": "2019-11-21 15:34:10.846093", + "links": [], + "modified": "2020-12-13 23:18:47.442466", "modified_by": "Administrator", "module": "Stock", "name": "Landed Cost Voucher", diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index bc3d3266ad..e39208991b 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -9,6 +9,7 @@ from frappe.model.meta import get_field_precision from frappe.model.document import Document from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.accounts.doctype.account.account import get_account_currency +from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate class LandedCostVoucher(Document): def get_items_from_purchase_receipts(self): @@ -39,12 +40,13 @@ class LandedCostVoucher(Document): def validate(self): self.check_mandatory() + self.set_exchange_rate() + self.set_amounts_in_company_currency() if not self.get("items"): self.get_items_from_purchase_receipts() else: self.validate_applicable_charges_for_item() self.validate_purchase_receipts() - self.validate_expense_accounts() self.set_total_taxes_and_charges() def check_mandatory(self): @@ -73,16 +75,25 @@ class LandedCostVoucher(Document): frappe.throw(_("Row {0}: Cost center is required for an item {1}") .format(item.idx, item.item_code)) - def validate_expense_accounts(self): - company_currency = erpnext.get_company_currency(self.company) - for account in self.taxes: - if get_account_currency(account.expense_account) != company_currency: - frappe.throw(msg=_(""" Row {0}: Expense account currency should be same as company's default currency. - Please select expense account with account currency as {1}""") - .format(account.idx, frappe.bold(company_currency)), title=_("Invalid Account Currency")) - def set_total_taxes_and_charges(self): - self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("taxes")]) + self.total_taxes_and_charges = sum([flt(d.base_amount) for d in self.get("taxes")]) + + def set_exchange_rate(self): + company_currency = erpnext.get_company_currency(self.company) + for d in self.get('taxes'): + if d.account_currency == company_currency: + d.exchange_rate = 1 + elif not d.exchange_rate or d.exchange_rate == 1 or self.posting_date: + d.exchange_rate = get_exchange_rate(self.posting_date, account=d.expense_account, + account_currency=d.account_currency, company=self.company) + + if not d.exchange_rate: + frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx)) + + def set_amounts_in_company_currency(self): + for d in self.get('taxes'): + d.amount = flt(d.amount, d.precision("amount")) + d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount")) def validate_applicable_charges_for_item(self): based_on = self.distribute_charges_based_on.lower() 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 260/728] 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 261/728] 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 262/728] 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 263/728] 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 264/728] 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 23f0debf8807eeac894cfb5d628fddb80bf0fd72 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 15 Dec 2020 10:00:21 +0530 Subject: [PATCH 265/728] fix: tests --- erpnext/projects/doctype/project/test_project.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py index f9bb1b3ac4..ea54774d52 100644 --- a/erpnext/projects/doctype/project/test_project.py +++ b/erpnext/projects/doctype/project/test_project.py @@ -79,16 +79,16 @@ class TestProject(unittest.TestCase): if not task2: task2 = create_task(subject="Test Temp Task with dependency", depends_on=task1.name, is_template=1, begin=2, duration=2) - template = make_project_template("Test Project with Templ - dependent tasks", [task2]) - project = get_project("Test Project with Templ - tasks with parent-child", template) + template = make_project_template("Test Project with Templ - dependent tasks", [task1, task2]) + project = get_project("Test Project with Templ - dependent tasks", template) tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc') - self.assertEqual(tasks[0].subject, 'Test Temp Task with dependency') - self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, tasks[0])) - self.assertEqual(tasks[0].depends_on, tasks[1].name) + self.assertEqual(tasks[1].subject, 'Test Temp Task with dependency') + self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, tasks[1])) + self.assertTrue(tasks[1].depends_on_tasks.find(tasks[0].name) >= 0 ) - self.assertEqual(tasks[1].subject, 'Test Temp Task for dependency') - self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, tasks[1]) ) + self.assertEqual(tasks[0].subject, 'Test Temp Task for dependency') + self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, tasks[0]) ) self.assertEqual(len(tasks), 2) From caf67e608f871adca275e001dddc96c96af4ea77 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 15 Dec 2020 10:00:31 +0530 Subject: [PATCH 266/728] fix: tests --- erpnext/projects/doctype/project/project.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 2d3339773a..5a9375a0e6 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -82,31 +82,29 @@ class Project(Document): def dependency_mapping(self, template_tasks, project_tasks): for tmp_task in template_tasks: - for prj_task in project_tasks: - if tmp_task.subject == prj_task.subject: - self.check_depends_on_value(tmp_task, prj_task, project_tasks) - self.check_for_parent_tasks(tmp_task, prj_task, project_tasks) + prj_task = list(filter(lambda x: x.subject == tmp_task.subject, project_tasks))[0] + self.check_depends_on_value(tmp_task, prj_task, project_tasks) + self.check_for_parent_tasks(tmp_task, prj_task, project_tasks) def check_depends_on_value(self, tmp_task, prj_task, project_tasks): - if tmp_task.depends_on and not prj_task.depends_on: - for child_task in tmp_task.depends_on: + if tmp_task.get("depends_on") and not prj_task.get("depends_on"): + for child_task in tmp_task.get("depends_on"): child_task_subject = frappe.db.get_value("Task", child_task.task, "subject") corresponding_prj_task = list(filter(lambda x: x.subject == child_task_subject, project_tasks)) if len(corresponding_prj_task): prj_task.append("depends_on",{ "task": corresponding_prj_task[0].name }) + print(prj_task.name) prj_task.save() def check_for_parent_tasks(self, tmp_task, prj_task, project_tasks): - if tmp_task.parent_task and not prj_task.parent_task: - parent_task_subject = frappe.db.get_value("Task", tmp_task.parent_task, "subject") + if tmp_task.get("parent_task") and not prj_task.get("parent_task"): + parent_task_subject = frappe.db.get_value("Task", tmp_task.get("parent_task"), "subject") corresponding_prj_task = list(filter(lambda x: x.subject == parent_task_subject, project_tasks)) if len(corresponding_prj_task): prj_task.parent_task = corresponding_prj_task[0].name - print(prj_task.name, prj_task.parent_task, corresponding_prj_task[0].name) prj_task.save() - print(prj_task.name, corresponding_prj_task[0].name) def is_row_updated(self, row, existing_task_data, fields): if self.get("__islocal") or not existing_task_data: return True From a6fef7ae6bbdba8a4f922ebdfbf337033c41ac4d Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 15 Dec 2020 11:50:18 +0530 Subject: [PATCH 267/728] feat: parent-child relation tasks --- erpnext/projects/doctype/project/project.py | 2 +- erpnext/projects/doctype/project/test_project.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 5a9375a0e6..13e72fec8a 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -83,6 +83,7 @@ class Project(Document): def dependency_mapping(self, template_tasks, project_tasks): for tmp_task in template_tasks: prj_task = list(filter(lambda x: x.subject == tmp_task.subject, project_tasks))[0] + prj_task = frappe.get_doc("Task", prj_task.name) self.check_depends_on_value(tmp_task, prj_task, project_tasks) self.check_for_parent_tasks(tmp_task, prj_task, project_tasks) @@ -95,7 +96,6 @@ class Project(Document): prj_task.append("depends_on",{ "task": corresponding_prj_task[0].name }) - print(prj_task.name) prj_task.save() def check_for_parent_tasks(self, tmp_task, prj_task, project_tasks): diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py index ea54774d52..c3f56b8e86 100644 --- a/erpnext/projects/doctype/project/test_project.py +++ b/erpnext/projects/doctype/project/test_project.py @@ -49,10 +49,10 @@ class TestProject(unittest.TestCase): if not task3: task3 = create_task(subject="Test Temp Task child 2", parent_task=task1.name, is_template=1, begin=2, duration=3) - template = make_project_template("Test Project Template - tasks with parent-child", [task1]) + template = make_project_template("Test Project Template - tasks with parent-child", [task1, task2, task3]) project = get_project("Test Project with Templ - tasks with parent-child", template) tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc') - print(tasks[0].duration) + self.assertEqual(tasks[0].subject, 'Test Temp Task parent') self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, tasks[0])) From d050816e17af897fedf40fb8bee5c1e0a0d05dcb Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 15 Dec 2020 12:02:26 +0530 Subject: [PATCH 268/728] 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 269/728] 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 270/728] 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 271/728] 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 272/728] 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 87b477a31126e478c2bcc77861975e015474bc6a Mon Sep 17 00:00:00 2001 From: pateljannat Date: Wed, 16 Dec 2020 13:37:21 +0530 Subject: [PATCH 273/728] feat: patch for project template tasks --- erpnext/patches.txt | 1 + .../v13_0/update_project_template_tasks.py | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 erpnext/patches/v13_0/update_project_template_tasks.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 86ac613ae5..435511210b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -741,3 +741,4 @@ erpnext.patches.v13_0.updates_for_multi_currency_payroll erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy erpnext.patches.v13_0.add_po_to_global_search erpnext.patches.v13_0.update_returned_qty_in_pr_dn +erpnext.patches.v13_0.update_project_template_tasks diff --git a/erpnext/patches/v13_0/update_project_template_tasks.py b/erpnext/patches/v13_0/update_project_template_tasks.py new file mode 100644 index 0000000000..55f0ff4505 --- /dev/null +++ b/erpnext/patches/v13_0/update_project_template_tasks.py @@ -0,0 +1,32 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + templates = frappe.get_list("Project Template", fields = ["name"]) + for template_name in templates: + template = frappe.get_doc("Project Template", template_name) + replace_tasks = False + new_tasks = [] + for task in template.tasks: + if task.subject: + replace_tasks = True + new_task = frappe.get_doc(dict( + doctype = "Task", + subject = task.subject, + start = task.start, + duration = task.duration, + task_weight = task.task_weight, + description = task.description, + is_template = 1 + )).insert() + new_tasks.append(new_task.name) + if replace_tasks: + template.tasks = [] + for tsk in new_tasks: + template.append("tasks", { + "task": tsk + }) + template.save() \ No newline at end of file From 9962ba86d0db913d53bf87736e1fdd2194436f09 Mon Sep 17 00:00:00 2001 From: "hasnain2808@gmail.com" Date: Wed, 16 Dec 2020 14:41:04 +0530 Subject: [PATCH 274/728] fix: charts not displaying when tree_type changed --- .../purchase_analytics/purchase_analytics.js | 72 +++++++++-------- .../report/sales_analytics/sales_analytics.js | 79 ++++++++++--------- 2 files changed, 81 insertions(+), 70 deletions(-) diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.js b/erpnext/buying/report/purchase_analytics/purchase_analytics.js index e17973c337..7ee9f2c372 100644 --- a/erpnext/buying/report/purchase_analytics/purchase_analytics.js +++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.js @@ -75,62 +75,66 @@ frappe.query_reports["Purchase Analytics"] = { return Object.assign(options, { checkboxColumn: true, events: { - onCheckRow: function(data) { + onCheckRow: function (data) { + if (!data) return; + + const data_doctype = $( + data[2].html + )[0].attributes.getNamedItem("data-doctype").value; + const tree_type = frappe.query_report.filters[0].value; + if (data_doctype != tree_type) return; + row_name = data[2].content; length = data.length; - var tree_type = frappe.query_report.filters[0].value; - - if(tree_type == "Supplier" || tree_type == "Item") { - row_values = data.slice(4,length-1).map(function (column) { - return column.content; - }) - } - else { - row_values = data.slice(3,length-1).map(function (column) { - return column.content; - }) + if (tree_type == "Supplier" || tree_type == "Item") { + row_values = data + .slice(4, length - 1) + .map(function (column) { + return column.content; + }); + } else { + row_values = data + .slice(3, length - 1) + .map(function (column) { + return column.content; + }); } - entry = { - 'name':row_name, - 'values':row_values - } + entry = { + name: row_name, + values: row_values, + }; let raw_data = frappe.query_report.chart.data; let new_datasets = raw_data.datasets; - var found = false; + let found = false; - for(var i=0; i < new_datasets.length;i++){ - if(new_datasets[i].name == row_name){ + for (let i = 0; i < new_datasets.length; i++) { + if (new_datasets[i].name == row_name) { found = true; - new_datasets.splice(i,1); + new_datasets.splice(i, 1); break; } } - if(!found){ + if (!found) { new_datasets.push(entry); } - let new_data = { labels: raw_data.labels, - datasets: new_datasets - } - - setTimeout(() => { - frappe.query_report.chart.update(new_data) - },500) - - - setTimeout(() => { - frappe.query_report.chart.draw(true); - }, 1000) + datasets: new_datasets, + }; + chart_options = { + data: new_data, + type: "line", + }; + frappe.query_report.render_chart(chart_options); frappe.query_report.raw_chart_data = new_data; }, - } + }, }); } } diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js index 0e565a3fb6..aad6bfd5ef 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.js +++ b/erpnext/selling/report/sales_analytics/sales_analytics.js @@ -74,67 +74,74 @@ frappe.query_reports["Sales Analytics"] = { return Object.assign(options, { checkboxColumn: true, events: { - onCheckRow: function(data) { + onCheckRow: function (data) { + if (!data) return; + + const data_doctype = $( + data[2].html + )[0].attributes.getNamedItem("data-doctype").value; + const tree_type = frappe.query_report.filters[0].value; + if (data_doctype != tree_type) return; + row_name = data[2].content; length = data.length; - var tree_type = frappe.query_report.filters[0].value; - - if(tree_type == "Customer") { - row_values = data.slice(4,length-1).map(function (column) { - return column.content; - }) + if (tree_type == "Customer") { + row_values = data + .slice(4, length - 1) + .map(function (column) { + return column.content; + }); } else if (tree_type == "Item") { - row_values = data.slice(5,length-1).map(function (column) { - return column.content; - }) - } - else { - row_values = data.slice(3,length-1).map(function (column) { - return column.content; - }) + row_values = data + .slice(5, length - 1) + .map(function (column) { + return column.content; + }); + } else { + row_values = data + .slice(3, length - 1) + .map(function (column) { + return column.content; + }); } entry = { - 'name':row_name, - 'values':row_values - } + name: row_name, + values: row_values, + }; let raw_data = frappe.query_report.chart.data; let new_datasets = raw_data.datasets; - var found = false; + let found = false; - for(var i=0; i < new_datasets.length;i++){ - if(new_datasets[i].name == row_name){ + for (let i = 0; i < new_datasets.length; i++) { + if (new_datasets[i].name == row_name) { found = true; - new_datasets.splice(i,1); + new_datasets.splice(i, 1); break; } } - if(!found){ + if (!found) { new_datasets.push(entry); } let new_data = { labels: raw_data.labels, - datasets: new_datasets - } - - setTimeout(() => { - frappe.query_report.chart.update(new_data) - }, 500) - - - setTimeout(() => { - frappe.query_report.chart.draw(true); - }, 1000) + datasets: new_datasets, + }; + chart_options = { + data: new_data, + type: "line", + }; + frappe.query_report.render_chart(chart_options); frappe.query_report.raw_chart_data = new_data; }, - } - }) + }, + }); }, } From b184d43e757f2982aab9b900943c789920609f8c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 16 Dec 2020 14:51:42 +0530 Subject: [PATCH 275/728] 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 924f99bead32bb4eba656019a15931f797eb04ae Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 16 Dec 2020 15:42:50 +0530 Subject: [PATCH 276/728] fix: Help message --- .../accounting_dimension_filter.js | 12 ++++++++++++ .../accounting_dimension_filter.json | 19 +++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js index a2526e92c3..74b7b51676 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js @@ -6,6 +6,18 @@ frappe.ui.form.on('Accounting Dimension Filter', { if (frm.doc.accounting_dimension) { frm.set_df_property('dimensions', 'label', frm.doc.accounting_dimension, cdn, 'dimension_value'); } + + let help_content = + `
  • {%= __(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"]) %}
    + +
    +

    + + {{__('Note: On checking Is Mandatory the accounting dimension will become mandatory against that specific account for all accounting transactions')}} +

    +
    `; + + frm.set_df_property('dimension_filter_help', 'options', help_content); }, onload: function(frm) { frm.set_query('applicable_on_account', 'accounts', function() { diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json index 7736b2dffb..c0327ad0ad 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json @@ -14,7 +14,9 @@ "section_break_4", "accounts", "column_break_6", - "dimensions" + "dimensions", + "section_break_10", + "dimension_filter_help" ], "fields": [ { @@ -89,11 +91,24 @@ "reqd": 1, "show_days": 1, "show_seconds": 1 + }, + { + "fieldname": "dimension_filter_help", + "fieldtype": "HTML", + "label": "Dimension Filter Help", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2020-11-24 12:34:42.458713", + "modified": "2020-12-16 15:27:23.659285", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting Dimension Filter", From 5a8a52b9c6739f25bbd3b128402cb5a19b40afc0 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 16 Dec 2020 15:54:06 +0530 Subject: [PATCH 277/728] fix: Therapy Type and Therapy Plan field visibility in Patient Appointment --- .../patient_appointment/patient_appointment.json | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index ac35acc21a..35600e4809 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -23,9 +23,9 @@ "procedure_template", "get_procedure_from_encounter", "procedure_prescription", + "therapy_plan", "therapy_type", "get_prescribed_therapies", - "therapy_plan", "practitioner", "practitioner_name", "department", @@ -284,7 +284,7 @@ "report_hide": 1 }, { - "depends_on": "eval:doc.patient;", + "depends_on": "eval:doc.patient && doc.therapy_plan;", "fieldname": "therapy_type", "fieldtype": "Link", "label": "Therapy", @@ -292,17 +292,16 @@ "set_only_once": 1 }, { - "depends_on": "eval:doc.patient && doc.__islocal;", + "depends_on": "eval:doc.patient && doc.therapy_plan && doc.__islocal;", "fieldname": "get_prescribed_therapies", "fieldtype": "Button", "label": "Get Prescribed Therapies" }, { - "depends_on": "eval: doc.patient && doc.therapy_type", + "depends_on": "eval: doc.patient;", "fieldname": "therapy_plan", "fieldtype": "Link", "label": "Therapy Plan", - "mandatory_depends_on": "eval: doc.patient && doc.therapy_type", "options": "Therapy Plan" }, { @@ -348,7 +347,7 @@ } ], "links": [], - "modified": "2020-05-21 03:04:21.400893", + "modified": "2020-12-16 13:16:58.578503", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", From f2a431d86615a5f3c509605dfb58c62adb24b366 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 16 Dec 2020 16:09:58 +0530 Subject: [PATCH 278/728] fix: filter Therapy Types and Therapy Plan in Patient Appointment --- .../patient_appointment.js | 30 +++++++++++++++++++ .../patient_appointment.py | 13 +++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index 2d6b64532b..79e1775b9d 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -22,6 +22,7 @@ frappe.ui.form.on('Patient Appointment', { filters: {'status': 'Active'} }; }); + frm.set_query('practitioner', function() { return { filters: { @@ -29,6 +30,7 @@ frappe.ui.form.on('Patient Appointment', { } }; }); + frm.set_query('service_unit', function(){ return { filters: { @@ -39,6 +41,16 @@ frappe.ui.form.on('Patient Appointment', { }; }); + frm.set_query('therapy_plan', function() { + return { + filters: { + 'patient': frm.doc.patient + } + }; + }); + + frm.trigger('set_therapy_type_filter'); + if (frm.is_new()) { frm.page.set_primary_action(__('Check Availability'), function() { if (!frm.doc.patient) { @@ -136,6 +148,24 @@ frappe.ui.form.on('Patient Appointment', { } }, + therapy_plan: function(frm) { + frm.trigger('set_therapy_type_filter'); + }, + + set_therapy_type_filter: function(frm) { + if (frm.doc.therapy_plan) { + frm.call('get_therapy_types').then(r => { + frm.set_query('therapy_type', function() { + return { + filters: { + 'name': ['in', r.message] + } + }; + }); + }); + } + }, + therapy_type: function(frm) { if (frm.doc.therapy_type) { frappe.db.get_value('Therapy Type', frm.doc.therapy_type, 'default_duration', (r) => { diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index e685b20a8c..dc820cb464 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -91,6 +91,17 @@ class PatientAppointment(Document): if fee_validity: frappe.msgprint(_('{0} has fee validity till {1}').format(self.patient, fee_validity.valid_till)) + def get_therapy_types(self): + if not self.therapy_plan: + return + + therapy_types = [] + doc = frappe.get_doc('Therapy Plan', self.therapy_plan) + for entry in doc.therapy_plan_details: + therapy_types.append(entry.therapy_type) + + return therapy_types + @frappe.whitelist() def check_payment_fields_reqd(patient): @@ -145,7 +156,7 @@ def invoice_appointment(appointment_doc): sales_invoice.flags.ignore_mandatory = True sales_invoice.save(ignore_permissions=True) sales_invoice.submit() - frappe.msgprint(_('Sales Invoice {0} created'.format(sales_invoice.name)), alert=True) + frappe.msgprint(_('Sales Invoice {0} created').format(sales_invoice.name), alert=True) frappe.db.set_value('Patient Appointment', appointment_doc.name, 'invoiced', 1) frappe.db.set_value('Patient Appointment', appointment_doc.name, 'ref_sales_invoice', sales_invoice.name) From d44f45c57be854c1c6c625ffccf86b56203c3dd7 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Wed, 16 Dec 2020 16:28:09 +0530 Subject: [PATCH 279/728] fix: sider issues --- erpnext/projects/doctype/project/test_project.py | 7 ++++--- erpnext/projects/doctype/task/task.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py index c3f56b8e86..ce56a50b4e 100644 --- a/erpnext/projects/doctype/project/test_project.py +++ b/erpnext/projects/doctype/project/test_project.py @@ -106,17 +106,18 @@ def get_project(name, template): def make_project(args): args = frappe._dict(args) - if args.project_template_name: - template = make_project_template(args.project_template_name) project = frappe.get_doc(dict( doctype = 'Project', project_name = args.project_name, status = 'Open', - project_template = template.name, expected_start_date = args.start_date )) + if args.project_template_name: + template = make_project_template(args.project_template_name) + project.project_template = template.name + if not frappe.db.exists("Project", args.project_name): project.insert() diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 072a848f26..80b764ba4f 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -94,7 +94,7 @@ class Task(NestedSet): def update_depends_on(self): depends_on_tasks = self.depends_on_tasks or "" for d in self.depends_on: - if d.task and not d.task in depends_on_tasks: + if d.task and d.task not in depends_on_tasks: depends_on_tasks += d.task + "," self.depends_on_tasks = depends_on_tasks @@ -180,7 +180,7 @@ class Task(NestedSet): def populate_depends_on(self): if self.parent_task: parent = frappe.get_doc('Task', self.parent_task) - if not self.name in [row.task for row in parent.depends_on]: + if self.name not in [row.task for row in parent.depends_on]: parent.append("depends_on", { "doctype": "Task Depends On", "task": self.name, From ff59f18012c0587c5c76ef3bdb74c72ebcff4957 Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 28 Oct 2020 11:02:02 +0530 Subject: [PATCH 280/728] 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 23d6afe43a83fc60fecc6d9009ea271e1faf0e6e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 16 Dec 2020 18:21:08 +0530 Subject: [PATCH 281/728] 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 2528d5ee15a5ce9d5d9634eec016946b1416154d Mon Sep 17 00:00:00 2001 From: pateljannat Date: Wed, 16 Dec 2020 18:29:49 +0530 Subject: [PATCH 282/728] fix: tests --- erpnext/projects/doctype/task/test_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py index d43d132e80..aded78b857 100644 --- a/erpnext/projects/doctype/task/test_task.py +++ b/erpnext/projects/doctype/task/test_task.py @@ -104,7 +104,7 @@ def create_task(subject, start=None, end=None, depends_on=None, project=None, pa task.subject = subject task.exp_start_date = start or nowdate() task.exp_end_date = end or nowdate() - task.project = project + task.project = project or "_Test Project" task.is_template = is_template task.start = begin task.duration = duration From 21168eab7f5922c7ea653b0883a68b5151acccb5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 16 Dec 2020 19:39:34 +0530 Subject: [PATCH 283/728] 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 86ac613ae5..9e33014c38 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 70cfc4df15cbbea19c32a61d0f73edc075a3699e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 16 Dec 2020 20:55:47 +0530 Subject: [PATCH 284/728] 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 c9f63accddd6a7b24d6c3ff257d8fba3395c8c94 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 17 Dec 2020 00:12:17 +0530 Subject: [PATCH 285/728] fix: do not manufacture same serial no multiple times --- erpnext/stock/doctype/serial_no/serial_no.py | 24 +++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 295149e238..25ce2d5969 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -6,7 +6,7 @@ import frappe import json from frappe.model.naming import make_autoname -from frappe.utils import cint, cstr, flt, add_days, nowdate, getdate +from frappe.utils import cint, cstr, flt, add_days, nowdate, getdate, get_link_to_form from erpnext.stock.get_item_details import get_reserved_qty_for_so from frappe import _, ValidationError @@ -244,7 +244,7 @@ def validate_serial_no(sle, item_det): for serial_no in serial_nos: if frappe.db.exists("Serial No", serial_no): sr = frappe.db.get_value("Serial No", serial_no, ["name", "item_code", "batch_no", "sales_order", - "delivery_document_no", "delivery_document_type", "warehouse", + "delivery_document_no", "delivery_document_type", "warehouse", "purchase_document_type", "purchase_document_no", "company"], as_dict=1) if sr and cint(sle.actual_qty) < 0 and sr.warehouse != sle.warehouse: @@ -256,9 +256,10 @@ def validate_serial_no(sle, item_det): frappe.throw(_("Serial No {0} does not belong to Item {1}").format(serial_no, sle.item_code), SerialNoItemError) - if cint(sle.actual_qty) > 0 and has_duplicate_serial_no(sr, sle): - frappe.throw(_("Serial No {0} has already been received").format(serial_no), - SerialNoDuplicateError) + if cint(sle.actual_qty) > 0 and has_serial_no_exists(sr, sle): + doc_name = frappe.bold(get_link_to_form(sr.purchase_document_type, sr.purchase_document_no)) + frappe.throw(_("Serial No {0} has already been received in the {1} #{2}") + .format(frappe.bold(serial_no), sr.purchase_document_type, doc_name), SerialNoDuplicateError) if (sr.delivery_document_no and sle.voucher_type not in ['Stock Entry', 'Stock Reconciliation'] and sle.voucher_type == sr.delivery_document_type): @@ -349,7 +350,7 @@ def validate_so_serial_no(sr, sales_order): frappe.throw(_("""{0} Serial No {1} cannot be delivered""") .format(msg, sr.name)) -def has_duplicate_serial_no(sn, sle): +def has_serial_no_exists(sn, sle): if (sn.warehouse and not sle.skip_serial_no_validaiton and sle.voucher_type != 'Stock Reconciliation'): return True @@ -359,12 +360,13 @@ def has_duplicate_serial_no(sn, sle): status = False if sn.purchase_document_no: - if sle.voucher_type in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"] and \ - sn.delivery_document_type not in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"]: + if (sle.voucher_type in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"] and + sn.delivery_document_type not in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"]): status = True - if status and sle.voucher_type == 'Stock Entry' and \ - frappe.db.get_value('Stock Entry', sle.voucher_no, 'purpose') != 'Material Receipt': + # If status is receipt then system will allow to in-ward the delivered serial no + if (status and sle.voucher_type == 'Stock Entry' and + frappe.db.get_value('Stock Entry', sle.voucher_no, 'purpose') == 'Material Receipt'): status = False return status @@ -420,7 +422,7 @@ def auto_make_serial_nos(args): if is_new: created_numbers.append(sr.name) - form_links = list(map(lambda d: frappe.utils.get_link_to_form('Serial No', d), created_numbers)) + form_links = list(map(lambda d: get_link_to_form('Serial No', d), created_numbers)) # Setting up tranlated title field for all cases singular_title = _("Serial Number Created") From 5a2030c2f22e1af5eeedcab053978758826dd34b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 17 Dec 2020 09:21:11 +0530 Subject: [PATCH 286/728] fix: Add server side methods and other fixes --- erpnext/controllers/accounts_controller.py | 2 +- .../landed_cost_voucher.js | 19 ++++--- .../landed_cost_voucher.py | 38 ++++++++++++-- .../test_landed_cost_voucher.py | 51 +++++++++++++++++-- .../purchase_receipt/purchase_receipt.py | 16 ++++-- 5 files changed, 106 insertions(+), 20 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 32c5d3a3b1..49c9f8c9d0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -442,7 +442,7 @@ class AccountsController(TransactionBase): account_currency = get_account_currency(gl_dict.account) if gl_dict.account and self.doctype not in ["Journal Entry", - "Period Closing Voucher", "Payment Entry"]: + "Period Closing Voucher", "Payment Entry", "Purchase Receipt", "Purchase Invoice"]: self.validate_account_currency(gl_dict.account, account_currency) set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"), self.company_currency) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js index a5ac60c4f5..82b5242704 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js @@ -96,9 +96,9 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ set_total_taxes_and_charges: function() { var total_taxes_and_charges = 0.0; $.each(this.frm.doc.taxes || [], function(i, d) { - total_taxes_and_charges += flt(d.base_amount) + total_taxes_and_charges += flt(d.base_amount); }); - cur_frm.set_value("total_taxes_and_charges", total_taxes_and_charges); + this.frm.set_value("total_taxes_and_charges", total_taxes_and_charges); }, set_applicable_charges_for_item: function() { @@ -148,11 +148,16 @@ frappe.ui.form.on('Landed Cost Voucher', { } }, + onload: function(frm) { + let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; + frm.set_currency_labels(["total_taxes_and_charges"], company_currency); + }, + set_exchange_rate: function(frm, cdt, cdn) { let row = locals[cdt][cdn]; let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; - if(row.account_currency == company_currency) { + if (row.account_currency == company_currency) { row.exchange_rate = 1; } else if (!row.exchange_rate || row.exchange_rate == 1) { frappe.call({ @@ -164,12 +169,12 @@ frappe.ui.form.on('Landed Cost Voucher', { company: frm.doc.company }, callback: function(r) { - if(r.message) { + if (r.message) { frappe.model.set_value(cdt, cdn, "exchange_rate", r.message); } } - }) - } + }); + }; }, set_base_amount: function(frm, cdt, cdn) { @@ -177,7 +182,7 @@ frappe.ui.form.on('Landed Cost Voucher', { frappe.model.set_value(cdt, cdn, "base_amount", flt(flt(row.amount)*row.exchange_rate, precision("base_amount", row))); } -}) +}); frappe.ui.form.on('Landed Cost Taxes and Charges', { expense_account: function(frm, cdt, cdn) { diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index e39208991b..519077afa4 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -40,14 +40,17 @@ class LandedCostVoucher(Document): def validate(self): self.check_mandatory() + self.validate_purchase_receipts() + self.set_account_currency() self.set_exchange_rate() self.set_amounts_in_company_currency() + self.set_total_taxes_and_charges() if not self.get("items"): self.get_items_from_purchase_receipts() - else: - self.validate_applicable_charges_for_item() - self.validate_purchase_receipts() - self.set_total_taxes_and_charges() + + self.set_applicable_charges_on_item() + self.validate_applicable_charges_for_item() + def check_mandatory(self): if not self.get("purchase_receipts"): @@ -78,6 +81,33 @@ class LandedCostVoucher(Document): def set_total_taxes_and_charges(self): self.total_taxes_and_charges = sum([flt(d.base_amount) for d in self.get("taxes")]) + def set_applicable_charges_on_item(self): + if self.get('taxes'): + total_item_cost = 0.0 + total_charges = 0.0 + item_count = 0 + based_on_field = frappe.scrub(self.distribute_charges_based_on) + + for item in self.get('items'): + total_item_cost += item.get(based_on_field) + + for item in self.get('items'): + item.applicable_charges = flt(flt(item.get(based_on_field)) * flt(self.total_taxes_and_charges) / flt(total_item_cost), + item.precision('applicable_charges')) + total_charges += item.applicable_charges + item_count += 1 + + if total_charges != self.total_taxes_and_charges: + diff = self.total_taxes_and_charges - total_charges + self.get('items')[item_count - 1].applicable_charges += diff + + def set_account_currency(self): + company_currency = erpnext.get_company_currency(self.company) + for d in self.get('taxes'): + if not d.account_currency: + account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency') + d.account_currency = account_currency or company_currency + def set_exchange_rate(self): company_currency = erpnext.get_company_currency(self.company) for d in self.get('taxes'): diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py index 3f2c5daf66..79d306b5db 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py @@ -10,6 +10,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \ import set_perpetual_inventory, get_gl_entries, test_records as pr_test_records, make_purchase_receipt from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.account.test_account import get_inventory_account +from erpnext.accounts.doctype.account.test_account import create_account class TestLandedCostVoucher(unittest.TestCase): def test_landed_cost_voucher(self): @@ -27,7 +28,7 @@ class TestLandedCostVoucher(unittest.TestCase): }, fieldname=["qty_after_transaction", "stock_value"], as_dict=1) - submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) + create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) pr_lc_value = frappe.db.get_value("Purchase Receipt Item", {"parent": pr.name}, "landed_cost_voucher_amount") self.assertEqual(pr_lc_value, 25.0) @@ -89,7 +90,7 @@ class TestLandedCostVoucher(unittest.TestCase): }, fieldname=["qty_after_transaction", "stock_value"], as_dict=1) - submit_landed_cost_voucher("Purchase Invoice", pi.name, pi.company) + create_landed_cost_voucher("Purchase Invoice", pi.name, pi.company) pi_lc_value = frappe.db.get_value("Purchase Invoice Item", {"parent": pi.name}, "landed_cost_voucher_amount") @@ -137,7 +138,7 @@ class TestLandedCostVoucher(unittest.TestCase): serial_no_rate = frappe.db.get_value("Serial No", "SN001", "purchase_rate") - submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) + create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) serial_no = frappe.db.get_value("Serial No", "SN001", ["warehouse", "purchase_rate"], as_dict=1) @@ -160,7 +161,7 @@ class TestLandedCostVoucher(unittest.TestCase): }) pr.submit() - lcv = submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22) + lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22) self.assertEqual(lcv.items[0].applicable_charges, 41.07) self.assertEqual(lcv.items[2].applicable_charges, 41.08) @@ -206,6 +207,46 @@ class TestLandedCostVoucher(unittest.TestCase): self.assertEqual(pr.items[0].landed_cost_voucher_amount, 100) self.assertEqual(pr.items[1].landed_cost_voucher_amount, 100) + def test_multi_currency_lcv(self): + ## Create USD Shipping charges_account + usd_shipping = create_account(account_name="Shipping Charges USD", + parent_account="Duties and Taxes - TCP1", company="_Test Company with perpetual inventory", + account_currency="USD") + + pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", + supplier_warehouse = "Stores - TCP1") + pr.submit() + + lcv = make_landed_cost_voucher(company = pr.company, receipt_document_type = "Purchase Receipt", + receipt_document=pr.name, charges=100, do_not_save=True) + + lcv.append("taxes", { + "description": "Shipping Charges", + "expense_account": usd_shipping, + "amount": 10 + }) + + lcv.save() + lcv.submit() + pr.load_from_db() + + # Considering exchange rate from USD to INR as 62.9 + self.assertEqual(lcv.total_taxes_and_charges, 729) + self.assertEqual(pr.items[0].landed_cost_voucher_amount, 729) + + gl_entries = frappe.get_all("GL Entry", fields=["account", "credit", "credit_in_account_currency"], + filters={"voucher_no": pr.name, "account": ("in", ["Shipping Charges USD - TCP1", "Expenses Included In Valuation - TCP1"])}) + + expected_gl_entries = { + "Shipping Charges USD - TCP1": [629, 10], + "Expenses Included In Valuation - TCP1": [100, 100] + } + + for entry in gl_entries: + amounts = expected_gl_entries.get(entry.account) + self.assertEqual(entry.credit, amounts[0]) + self.assertEqual(entry.credit_in_account_currency, amounts[1]) + def make_landed_cost_voucher(** args): args = frappe._dict(args) ref_doc = frappe.get_doc(args.receipt_document_type, args.receipt_document) @@ -236,7 +277,7 @@ def make_landed_cost_voucher(** args): return lcv -def submit_landed_cost_voucher(receipt_document_type, receipt_document, company, charges=50): +def create_landed_cost_voucher(receipt_document_type, receipt_document, company, charges=50): ref_doc = frappe.get_doc(receipt_document_type, receipt_document) lcv = frappe.new_doc("Landed Cost Voucher") diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 97e0fa738c..2cb8d2b26d 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -279,12 +279,15 @@ class PurchaseReceipt(BuyingController): # Amount added through landed-cost-voucher if d.landed_cost_voucher_amount and landed_cost_entries: for account, amount in iteritems(landed_cost_entries[(d.item_code, d.name)]): + account_currency = get_account_currency(account) gl_entries.append(self.get_gl_dict({ "account": account, + "account_currency": account_currency, "against": warehouse_account[d.warehouse]["account"], "cost_center": d.cost_center, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "credit": flt(amount), + "credit": flt(amount["base_amount"]), + "credit_in_account_currency": flt(amount["amount"]), "project": d.project }, item=d)) @@ -729,9 +732,16 @@ def get_item_account_wise_additional_cost(purchase_document): if item.receipt_document == purchase_document: for account in landed_cost_voucher_doc.taxes: item_account_wise_cost.setdefault((item.item_code, item.purchase_receipt_item), {}) - item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(account.expense_account, 0.0) - item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account] += \ + item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(account.expense_account, { + "amount": 0.0, + "base_amount": 0.0 + }) + + item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account]["amount"] += \ account.amount * item.get(based_on_field) / total_item_cost + item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account]["base_amount"] += \ + account.base_amount * item.get(based_on_field) / total_item_cost + return item_account_wise_cost From d6cd02d29d26bc814328ded38f12c090a27a6970 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 17 Dec 2020 09:21:24 +0530 Subject: [PATCH 287/728] fix: Add tests --- erpnext/accounts/doctype/account/test_account.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index 0605d89a7e..fbcaa6cc18 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -254,7 +254,8 @@ def create_account(**kwargs): account_name = kwargs.get('account_name'), account_type = kwargs.get('account_type'), parent_account = kwargs.get('parent_account'), - company = kwargs.get('company') + company = kwargs.get('company'), + account_currency = kwargs.get('account_currency') )) account.save() From f2bff8e220a26b1ed9a662e010b7e15fe91df73e Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 17 Dec 2020 11:54:59 +0530 Subject: [PATCH 288/728] fix: patch relaod doctype --- erpnext/patches/v13_0/update_project_template_tasks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_project_template_tasks.py b/erpnext/patches/v13_0/update_project_template_tasks.py index 55f0ff4505..df1886f616 100644 --- a/erpnext/patches/v13_0/update_project_template_tasks.py +++ b/erpnext/patches/v13_0/update_project_template_tasks.py @@ -5,9 +5,10 @@ from __future__ import unicode_literals import frappe def execute(): + frappe.reload_doctype("Project Template") templates = frappe.get_list("Project Template", fields = ["name"]) for template_name in templates: - template = frappe.get_doc("Project Template", template_name) + template = frappe.get_doc("Project Template", template_name.name) replace_tasks = False new_tasks = [] for task in template.tasks: From 6f99c0850c3e40eff4a0c4f43ba850a1bacda620 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 17 Dec 2020 12:19:58 +0530 Subject: [PATCH 289/728] fix: Linting Issues --- .../stock/doctype/landed_cost_voucher/landed_cost_voucher.js | 2 +- .../stock/doctype/landed_cost_voucher/landed_cost_voucher.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js index 82b5242704..c817f05e5a 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js @@ -174,7 +174,7 @@ frappe.ui.form.on('Landed Cost Voucher', { } } }); - }; + } }, set_base_amount: function(frm, cdt, cdn) { diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index 519077afa4..ed54551595 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -193,8 +193,8 @@ class LandedCostVoucher(Document): docs = frappe.db.get_all('Asset', filters={ receipt_document_type: item.receipt_document, 'item_code': item.item_code }, fields=['name', 'docstatus']) if not docs or len(docs) != item.qty: - frappe.throw(_('There are not enough asset created or linked to {0}. \ - Please create or link {1} Assets with respective document.').format(item.receipt_document, item.qty)) + frappe.throw(_('There are not enough asset created or linked to {0}. Please create or link {1} Assets with respective document.').format( + item.receipt_document, item.qty)) if docs: for d in docs: if d.docstatus == 1: From 51892efd60e8cd86fde4d9167eca064f691d1b4f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 17 Dec 2020 12:35:22 +0530 Subject: [PATCH 290/728] fix: Translation issues --- .../stock/doctype/landed_cost_voucher/landed_cost_voucher.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index ed54551595..17bfa8fbb0 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -198,8 +198,7 @@ class LandedCostVoucher(Document): if docs: for d in docs: if d.docstatus == 1: - frappe.throw(_('{2} {0} has submitted Assets.\ - Remove Item {1} from table to continue.').format( + frappe.throw(_('{2} {0} has submitted Assets. Remove Item {1} from table to continue.').format( item.receipt_document, item.item_code, item.receipt_document_type)) def update_rate_in_serial_no_for_non_asset_items(self, receipt_document): From 1872e2c1ac429439de1c8d1f52c79b41e9dd7fdd Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 17 Dec 2020 14:29:52 +0530 Subject: [PATCH 291/728] 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 2dbb1d6bc72b28542eec44878c28f1eed069bcca Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 17 Dec 2020 15:49:52 +0530 Subject: [PATCH 292/728] fix: indentation --- erpnext/patches/v13_0/update_project_template_tasks.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v13_0/update_project_template_tasks.py b/erpnext/patches/v13_0/update_project_template_tasks.py index df1886f616..8dd0181ece 100644 --- a/erpnext/patches/v13_0/update_project_template_tasks.py +++ b/erpnext/patches/v13_0/update_project_template_tasks.py @@ -5,10 +5,9 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.reload_doctype("Project Template") - templates = frappe.get_list("Project Template", fields = ["name"]) - for template_name in templates: + for template_name in frappe.db.sql(""" select name from `tabProject Template` """, as_dict=1): template = frappe.get_doc("Project Template", template_name.name) + print(template.tasks) replace_tasks = False new_tasks = [] for task in template.tasks: From 611b42733b82a3ab737fde001f7ca9246f1e6870 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Thu, 17 Dec 2020 14:49:48 +0530 Subject: [PATCH 293/728] fix: leave policy dashboard fix and roles --- .../doctype/leave_policy/leave_policy_dashboard.py | 14 +------------- .../leave_policy_assignment.json | 5 ++++- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py index ff5dc2ff3e..e0ec4be2dc 100644 --- a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py +++ b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py @@ -4,22 +4,10 @@ from frappe import _ def get_data(): return { 'fieldname': 'leave_policy', - 'non_standard_fieldnames': { - 'Employee Grade': 'default_leave_policy' - }, 'transactions': [ - { - 'label': _('Employees'), - 'items': ['Employee', 'Employee Grade'] - }, { 'label': _('Leaves'), 'items': ['Leave Allocation'] }, ] - } - - - - - \ No newline at end of file + } \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json index ecebb3b7d6..bbb4222715 100644 --- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json +++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json @@ -111,7 +111,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-10-15 15:18:15.227848", + "modified": "2020-12-17 16:27:20.311060", "modified_by": "Administrator", "module": "HR", "name": "Leave Policy Assignment", @@ -127,6 +127,7 @@ "report": 1, "role": "HR Manager", "share": 1, + "submit": 1, "write": 1 }, { @@ -139,6 +140,7 @@ "report": 1, "role": "HR User", "share": 1, + "submit": 1, "write": 1 }, { @@ -151,6 +153,7 @@ "report": 1, "role": "System Manager", "share": 1, + "submit": 1, "write": 1 } ], From 09f0e9111d6fb79868c58f55ead0f18351c6d216 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 17 Dec 2020 17:20:21 +0530 Subject: [PATCH 294/728] fix: patch --- erpnext/patches/v13_0/update_project_template_tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_project_template_tasks.py b/erpnext/patches/v13_0/update_project_template_tasks.py index 8dd0181ece..0bcd1d3f3a 100644 --- a/erpnext/patches/v13_0/update_project_template_tasks.py +++ b/erpnext/patches/v13_0/update_project_template_tasks.py @@ -5,9 +5,9 @@ from __future__ import unicode_literals import frappe def execute(): + frappe.reload_doc("projects", "doctype", "project_template”) for template_name in frappe.db.sql(""" select name from `tabProject Template` """, as_dict=1): template = frappe.get_doc("Project Template", template_name.name) - print(template.tasks) replace_tasks = False new_tasks = [] for task in template.tasks: From 5a06908bbc247607e61d18f8fd848e1bceaa6e11 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 17 Dec 2020 17:38:53 +0530 Subject: [PATCH 295/728] fix: Add breadccrumbs to item group page --- erpnext/templates/generators/item_group.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html index 40a064fc76..74b2ae3c51 100644 --- a/erpnext/templates/generators/item_group.html +++ b/erpnext/templates/generators/item_group.html @@ -1,5 +1,9 @@ {% extends "templates/web.html" %} +{% block breadcrumbs %} + {% include "templates/includes/breadcrumbs.html" %} +{% endblock %} + {% block header %}

    {{ name }}

    {% endblock %} {% block page_content %} From 79b71462cbdec8fabbb20f80f7e258bb55a65620 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 17 Dec 2020 18:21:34 +0530 Subject: [PATCH 296/728] fix: patch --- erpnext/patches/v13_0/update_project_template_tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_project_template_tasks.py b/erpnext/patches/v13_0/update_project_template_tasks.py index 0bcd1d3f3a..1303efd93f 100644 --- a/erpnext/patches/v13_0/update_project_template_tasks.py +++ b/erpnext/patches/v13_0/update_project_template_tasks.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.reload_doc("projects", "doctype", "project_template”) + frappe.reload_doc("projects", "doctype", "project_template") for template_name in frappe.db.sql(""" select name from `tabProject Template` """, as_dict=1): template = frappe.get_doc("Project Template", template_name.name) replace_tasks = False From b074334dcff6d337351809fc991f01489424929e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 17 Dec 2020 18:46:59 +0530 Subject: [PATCH 297/728] fix: Typo in tax category doctype query --- erpnext/regional/india/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index f8520c2d00..f256a66266 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -53,7 +53,7 @@ def validate_gstin_for_india(doc, method): .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.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: From b8e656512e8ab34601149c3f3ca0f9831441545a Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 17 Dec 2020 20:22:06 +0530 Subject: [PATCH 298/728] fix: test cleanup --- .../project_template/test_project_template.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/erpnext/projects/doctype/project_template/test_project_template.py b/erpnext/projects/doctype/project_template/test_project_template.py index 6c6b78368e..95663cdcbb 100644 --- a/erpnext/projects/doctype/project_template/test_project_template.py +++ b/erpnext/projects/doctype/project_template/test_project_template.py @@ -10,19 +10,6 @@ from erpnext.projects.doctype.task.test_task import create_task class TestProjectTemplate(unittest.TestCase): pass -def get_project_template(project_template_name="Test Project Template", project_tasks=[]): - if not frappe.db.exists('Project Template', project_template_name): - frappe.get_doc(dict( - doctype = 'Project Template', - name = project_template_name, - tasks = project_tasks or [ - create_task(subject="_Test Template Task 1", is_template=1, begin=0, duration=3), - create_task(subject="_Test Template Task 2", is_template=1, begin=0, duration=2) - ] - )).insert() - - return frappe.get_doc('Project Template', project_template_name) - def make_project_template(project_template_name, project_tasks=[]): if not frappe.db.exists('Project Template', project_template_name): project_tasks = project_tasks or [ From 04f48a011d343cad2dbe667b68408db6d523982e Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Fri, 18 Dec 2020 01:31:00 +0530 Subject: [PATCH 299/728] feat: Add year_to_date field --- erpnext/payroll/doctype/salary_slip/salary_slip.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json index 386618cf08..b64e5a08fe 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.json +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json @@ -69,6 +69,7 @@ "net_pay_info", "net_pay", "base_net_pay", + "year_to_date", "column_break_53", "rounded_total", "base_rounded_total", @@ -578,13 +579,18 @@ { "fieldname": "column_break_69", "fieldtype": "Column Break" + }, + { + "fieldname": "year_to_date", + "fieldtype": "Currency", + "label": "Year To Date" } ], "icon": "fa fa-file-text", "idx": 9, "is_submittable": 1, "links": [], - "modified": "2020-10-21 23:02:59.400249", + "modified": "2020-12-17 21:51:19.612940", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Slip", From 9f1e018e4f868220a985af6784c1940a67d47b82 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Fri, 18 Dec 2020 01:35:27 +0530 Subject: [PATCH 300/728] feat: Compute year_to_date --- .../doctype/salary_slip/salary_slip.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 20365b191d..27de46acc3 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -35,6 +35,9 @@ class SalarySlip(TransactionBase): def autoname(self): self.name = make_autoname(self.series) + def before_save(self): + self.compute_year_to_date() + def validate(self): self.status = self.get_status() self.validate_dates() @@ -1125,6 +1128,26 @@ class SalarySlip(TransactionBase): self.gross_pay += self.earnings[i].amount self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) + def compute_year_to_date(self): + year_to_date = 0 + fiscal_year = frappe.get_list('Fiscal Year', + fields = ['year','year_start_date','year_end_date'], + filters= {'year_start_date' : ['<=', self.start_date], + 'year_end_date' : ['>=', self.end_date] + })[0] + salary_slips_from_current_fiscal_year = frappe.get_list('Salary Slip', + fields = ['employee_name', 'start_date', 'end_date', 'net_pay'], + filters = {'employee_name' : self.employee_name, + 'start_date' : ['>=', fiscal_year.year_start_date], + 'end_date' : ['<=', fiscal_year.year_end_date] + }) + + for salary_slip in salary_slips_from_current_fiscal_year: + year_to_date += salary_slip.net_pay + + year_to_date += self.net_pay + self.year_to_date = year_to_date + 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)) @@ -1135,4 +1158,4 @@ def unlink_ref_doc_from_salary_slip(ref_no): def generate_password_for_pdf(policy_template, employee): employee = frappe.get_doc("Employee", employee) - return policy_template.format(**employee.as_dict()) + return policy_template.format(**employee.as_dict()) \ No newline at end of file From 6afa83f2c7ac8a71c05959e21884ccab2733fa14 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 18 Dec 2020 11:05:41 +0530 Subject: [PATCH 301/728] fix(requirements): update to latest pandas --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c4f9171fca..678cf74fef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ braintree==3.57.1 frappe gocardless-pro==1.11.0 googlemaps==3.1.1 -pandas==1.0.5 +pandas>=1.0.5 plaid-python==6.0.0 pycountry==19.8.18 PyGithub==1.44.1 From a81519f5571f25d15c47e20eefce257c18c3f1ff Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 18 Dec 2020 11:16:01 +0530 Subject: [PATCH 302/728] fix: error popup for submitted doc --- .../payroll/doctype/salary_slip/salary_slip.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js index f7e22c6387..abe873d839 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.js +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js @@ -214,14 +214,16 @@ frappe.ui.form.on('Salary Slip Timesheet', { }); 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(); - } - }); + if (frm.doc.docstatus === 0) { + if (frm.doc.earnings || frm.doc.deductions) { + frappe.call({ + method: "set_totals", + doc: frm.doc, + callback: function() { + frm.refresh_fields(); + } + }); + } } }; From 2cd41bca065f14228a27f183068f936e0c9ea11f Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 18 Dec 2020 13:17:58 +0530 Subject: [PATCH 303/728] 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")}${__("Date")} ${course_schedules.map( - c => `${c.name} + c => `${c.name} ${c.schedule_date}` ).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 89a02d7d3f705742bc95e654e2909a821e883e45 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Fri, 18 Dec 2020 14:59:20 +0530 Subject: [PATCH 304/728] feat: Changed Fiscal Year to Payroll Period --- .../doctype/salary_slip/salary_slip.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 27de46acc3..0ea0684a8f 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -35,9 +35,6 @@ class SalarySlip(TransactionBase): def autoname(self): self.name = make_autoname(self.series) - def before_save(self): - self.compute_year_to_date() - def validate(self): self.status = self.get_status() self.validate_dates() @@ -52,6 +49,7 @@ class SalarySlip(TransactionBase): self.get_working_days_details(lwp = self.leave_without_pay) self.calculate_net_pay() + self.compute_year_to_date() 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") @@ -1130,19 +1128,21 @@ class SalarySlip(TransactionBase): def compute_year_to_date(self): year_to_date = 0 - fiscal_year = frappe.get_list('Fiscal Year', - fields = ['year','year_start_date','year_end_date'], - filters= {'year_start_date' : ['<=', self.start_date], - 'year_end_date' : ['>=', self.end_date] + payroll_period = frappe.get_list('Payroll Period', + fields = ['start_date','end_date','company'], + filters= {'start_date' : ['<=', self.start_date], + 'end_date' : ['>=', self.end_date], + 'company' : self.company })[0] - salary_slips_from_current_fiscal_year = frappe.get_list('Salary Slip', + salary_slips_from_current_payroll_period = frappe.get_list('Salary Slip', fields = ['employee_name', 'start_date', 'end_date', 'net_pay'], filters = {'employee_name' : self.employee_name, - 'start_date' : ['>=', fiscal_year.year_start_date], - 'end_date' : ['<=', fiscal_year.year_end_date] + 'start_date' : ['>=', payroll_period.start_date], + 'end_date' : ['<=', payroll_period.end_date], + 'name' : ['!=', self.name] }) - for salary_slip in salary_slips_from_current_fiscal_year: + for salary_slip in salary_slips_from_current_payroll_period: year_to_date += salary_slip.net_pay year_to_date += self.net_pay From d6277cdc7f08f14081b7e425f8a901472c4a73cb Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 18 Dec 2020 21:37:19 +0530 Subject: [PATCH 305/728] feat: Value Based and Numeric Quality Inspection - Acceptance Formula is optional - Choose between Value based and Numeric QI - If numeric, select single or multiple readings - Added Min, Max and Mean Values for numeric inspection to avoid formula usage - Deprecated code cleanup in js file --- .../item_quality_inspection_parameter.json | 54 +++++++++- .../quality_inspection/quality_inspection.js | 102 +++++++++--------- .../quality_inspection.json | 4 +- .../quality_inspection/quality_inspection.py | 98 +++++++++++++---- .../quality_inspection_reading.json | 93 ++++++++++++++-- .../quality_inspection_template.py | 4 +- 6 files changed, 268 insertions(+), 87 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 888bc2de47..f450128157 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 @@ -8,8 +8,14 @@ "field_order": [ "specification", "value", + "value_based", + "single_reading", "column_break_3", - "acceptance_formula" + "formula_based_criteria", + "acceptance_formula", + "min_value", + "max_value", + "mean_value" ], "fields": [ { @@ -24,10 +30,11 @@ "width": "200px" }, { + "depends_on": "eval:(!doc.formula_based_criteria && doc.value_based)", "fieldname": "value", "fieldtype": "Data", "in_list_view": 1, - "label": "Acceptance Criteria", + "label": "Acceptance Criteria Value", "oldfieldname": "value", "oldfieldtype": "Data" }, @@ -36,17 +43,56 @@ "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", + "depends_on": "formula_based_criteria", + "description": "Simple Python formula applied on Reading fields.
    Numeric eg.: reading_1 > 0.2 and reading_1 < 0.5
    \nValue based eg.: reading_value in (\"A\", \"B\", \"C)", "fieldname": "acceptance_formula", "fieldtype": "Code", "in_list_view": 1, "label": "Acceptance Criteria Formula" + }, + { + "default": "0", + "fieldname": "formula_based_criteria", + "fieldtype": "Check", + "label": "Formula Based Criteria" + }, + { + "default": "0", + "depends_on": "eval:!doc.value_based", + "fieldname": "single_reading", + "fieldtype": "Check", + "label": "Single Reading" + }, + { + "depends_on": "eval:(!doc.formula_based_criteria && !doc.single_reading && !doc.value_based)", + "fieldname": "mean_value", + "fieldtype": "Float", + "label": "Mean Value" + }, + { + "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "fieldname": "min_value", + "fieldtype": "Float", + "label": "Minimum Value" + }, + { + "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "fieldname": "max_value", + "fieldtype": "Float", + "label": "Maximum Value" + }, + { + "default": "0", + "description": "Non-numeric Inspection.", + "fieldname": "value_based", + "fieldtype": "Check", + "label": "Value Based" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-11-16 16:33:42.421842", + "modified": "2020-12-18 21:03:29.828723", "modified_by": "Administrator", "module": "Stock", "name": "Item Quality Inspection Parameter", diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.js b/erpnext/stock/doctype/quality_inspection/quality_inspection.js index 376848afaa..f0bf9aed80 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.js +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.js @@ -4,6 +4,54 @@ cur_frm.cscript.refresh = cur_frm.cscript.inspection_type; frappe.ui.form.on("Quality Inspection", { + setup: function(frm) { + frm.set_query("batch_no", function() { + return { + filters: { + "item": frm.doc.item_code + } + } + }); + + // Serial No based on item_code + frm.set_query("item_serial_no", function() { + var filters = {}; + if (frm.doc.item_code) { + filters = { + 'item_code': frm.doc.item_code + } + } + return { filters: filters } + }); + + // item code based on GRN/DN + frm.set_query("item_code", function(doc) { + 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: filters + }; + } + }); + }, + item_code: function(frm) { if (frm.doc.item_code) { return frm.call({ @@ -26,55 +74,5 @@ frappe.ui.form.on("Quality Inspection", { } }); } - } -}) - -// item code based on GRN/DN -cur_frm.fields_dict['item_code'].get_query = function(doc, cdt, cdn) { - 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: filters - }; - } -}, - -// Serial No based on item_code -cur_frm.fields_dict['item_serial_no'].get_query = function(doc, cdt, cdn) { - var filters = {}; - if (doc.item_code) { - filters = { - 'item_code': doc.item_code - } - } - return { filters: filters } -} - -cur_frm.set_query("batch_no", function(doc) { - return { - filters: { - "item": doc.item_code - } - } -}) - -cur_frm.add_fetch('item_code', 'item_name', 'item_name'); -cur_frm.add_fetch('item_code', 'description', 'description'); - + }, +}) \ No newline at end of file diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json index f6d76194d9..edfe7e98b2 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json @@ -136,6 +136,7 @@ "width": "50%" }, { + "fetch_from": "item_code.item_name", "fieldname": "item_name", "fieldtype": "Data", "in_global_search": 1, @@ -143,6 +144,7 @@ "read_only": 1 }, { + "fetch_from": "item_code.description", "fieldname": "description", "fieldtype": "Small Text", "label": "Description", @@ -236,7 +238,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-11-19 17:06:05.409963", + "modified": "2020-12-18 19:59:55.710300", "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 ae4eb9b995..a7a023bcbf 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -6,7 +6,7 @@ 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 frappe.utils import flt, cint from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template \ import get_template_details @@ -16,7 +16,7 @@ class QualityInspection(Document): self.get_item_specification_details() if self.readings: - self.set_status_based_on_acceptance_formula() + self.inspect_and_set_status() def get_item_specification_details(self): if not self.quality_inspection_template: @@ -29,9 +29,7 @@ class QualityInspection(Document): parameters = get_template_details(self.quality_inspection_template) for d in parameters: child = self.append('readings', {}) - child.specification = d.specification - child.value = d.value - child.acceptance_formula = d.acceptance_formula + child.update(d) child.status = "Accepted" def get_quality_inspection_template(self): @@ -76,28 +74,84 @@ 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): + def inspect_and_set_status(self): for reading in self.readings: - if not reading.acceptance_formula: continue + if reading.formula_based_criteria: + self.set_status_based_on_acceptance_formula(reading) + else: + self.set_status_based_on_acceptance_values(reading) + + def set_status_based_on_acceptance_values(self, reading): + if cint(reading.value_based): + result = reading.get("reading_value") == reading.get("value") + else: + # numeric readings + if cint(reading.single_reading): + reading_1 = flt(reading.get("reading_1")) + result = flt(reading.get("min_value")) <= reading_1 <= flt(reading.get("max_value")) + else: + result = self.min_max_criteria_passed(reading) and self.mean_criteria_passed(reading) + + reading.status = "Accepted" if result else "Rejected" + + def min_max_criteria_passed(self, reading): + """Determine whether all readings fall in the acceptable range.""" + for i in range(1, 11): + reading_field = reading.get("reading_" + str(i)) + if reading_field is not None: + result = flt(reading.get("min_value")) <= flt(reading_field) <= flt(reading.get("max_value")) + if not result: return False + return True + + def mean_criteria_passed(self, reading): + """Determine whether mean of all readings is acceptable.""" + if reading.get("mean_value"): + from statistics import mean + readings_list = [] - condition = reading.acceptance_formula - data = {} for i in range(1, 11): - field = "reading_" + str(i) - data[field] = flt(reading.get(field)) or 0 + reading_value = reading.get("reading_" + str(i)) + if reading_value is not None: + readings_list.append(flt(reading_value)) - 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")) + actual_mean = mean(readings_list) if readings_list else 0 + return True if actual_mean == reading.get("mean_value") else False + return True # no mean value, nothing to check + + def set_status_based_on_acceptance_formula(self, reading): + if not reading.acceptance_formula: + frappe.throw(_("Row #{0}: Acceptance Criteria Formula is required.").format(reading.idx), + title=_("Missing Formula")) + + condition = reading.acceptance_formula + data = self.get_formula_evaluation_data(reading) + + try: + result = frappe.safe_eval(condition, None, data) + reading.status = "Accepted" if result else "Rejected" + 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")) + except Exception: + frappe.throw(_("Row #{0}: Acceptance Criteria Formula is incorrect.").format(reading.idx), + title=_("Invalid Formula")) + + def get_formula_evaluation_data(self, reading): + data = {} + if cint(reading.value_based): + data = {"reading_value": reading.get("reading_value")} + else: + # numeric readings + data = {"reading_1": flt(reading.get("reading_1"))} + if not cint(reading.single_reading): + # if multiple numeric readings add all readings to data + for i in range(2, 11): + field = "reading_" + str(i) + data[field] = flt(reading.get(field)) + return data @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs 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 c1976dd1fb..db95fabee0 100644 --- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json +++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json @@ -7,21 +7,30 @@ "engine": "InnoDB", "field_order": [ "specification", - "value", "status", + "value", + "value_based", "column_break_4", + "formula_based_criteria", "acceptance_formula", + "min_value", + "max_value", + "mean_value", "section_break_3", + "reading_value", + "section_break_14", + "single_reading", + "section_break_12", "reading_1", "reading_2", "reading_3", - "column_break_10", "reading_4", + "column_break_10", "reading_5", "reading_6", - "column_break_14", "reading_7", "reading_8", + "column_break_14", "reading_9", "reading_10" ], @@ -38,10 +47,11 @@ }, { "columns": 2, + "depends_on": "eval:(!doc.formula_based_criteria && doc.value_based)", "fieldname": "value", "fieldtype": "Data", "in_list_view": 1, - "label": "Acceptance Criteria", + "label": "Acceptance Criteria Value", "oldfieldname": "value", "oldfieldtype": "Data" }, @@ -56,6 +66,7 @@ }, { "columns": 1, + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_2", "fieldtype": "Data", "in_list_view": 1, @@ -65,6 +76,7 @@ }, { "columns": 1, + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_3", "fieldtype": "Data", "in_list_view": 1, @@ -73,6 +85,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_4", "fieldtype": "Data", "label": "Reading 4", @@ -80,6 +93,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_5", "fieldtype": "Data", "label": "Reading 5", @@ -87,6 +101,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_6", "fieldtype": "Data", "label": "Reading 6", @@ -94,6 +109,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_7", "fieldtype": "Data", "label": "Reading 7", @@ -101,6 +117,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_8", "fieldtype": "Data", "label": "Reading 8", @@ -108,6 +125,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_9", "fieldtype": "Data", "label": "Reading 9", @@ -115,6 +133,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_10", "fieldtype": "Data", "label": "Reading 10", @@ -133,15 +152,18 @@ "options": "Accepted\nRejected" }, { + "depends_on": "value_based", "fieldname": "section_break_3", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Value Based Inspection" }, { "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", + "depends_on": "formula_based_criteria", + "description": "Simple Python formula applied on Reading fields.
    Numeric eg.: reading_1 > 0.2 and reading_1 < 0.5
    \nValue based eg.: reading_value in (\"A\", \"B\", \"C)", "fieldname": "acceptance_formula", "fieldtype": "Code", "label": "Acceptance Criteria Formula" @@ -153,12 +175,69 @@ { "fieldname": "column_break_14", "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "formula_based_criteria", + "fieldtype": "Check", + "label": "Formula Based Criteria" + }, + { + "depends_on": "eval:(!doc.formula_based_criteria && !doc.single_reading && !doc.value_based)", + "fieldname": "mean_value", + "fieldtype": "Float", + "label": "Mean Value" + }, + { + "default": "0", + "fieldname": "single_reading", + "fieldtype": "Check", + "label": "Single Reading" + }, + { + "depends_on": "eval:!doc.value_based", + "fieldname": "section_break_12", + "fieldtype": "Section Break", + "hide_border": 1 + }, + { + "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "description": "Applied on each reading.", + "fieldname": "min_value", + "fieldtype": "Float", + "label": "Minimum Value" + }, + { + "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "description": "Applied on each reading.", + "fieldname": "max_value", + "fieldtype": "Float", + "label": "Maximum Value" + }, + { + "default": "0", + "description": "Non-numeric Inspection.", + "fieldname": "value_based", + "fieldtype": "Check", + "label": "Value Based" + }, + { + "depends_on": "value_based", + "fieldname": "reading_value", + "fieldtype": "Data", + "label": "Reading Value" + }, + { + "depends_on": "eval:!doc.value_based", + "fieldname": "section_break_14", + "fieldtype": "Section Break", + "label": "Numeric Inspection" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-11-16 16:34:29.947856", + "modified": "2020-12-18 21:02:04.865777", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection Reading", 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 e2848469b8..7dd0febc20 100644 --- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py +++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py @@ -13,6 +13,8 @@ def get_template_details(template): if not template: return [] return frappe.get_all('Item Quality Inspection Parameter', - fields=["specification", "value", "acceptance_formula"], + fields=["specification", "value", "acceptance_formula", + "value_based", "formula_based_criteria", "single_reading", + "min_value", "max_value", "mean_value"], filters={'parenttype': 'Quality Inspection Template', 'parent': template}, order_by="idx") \ No newline at end of file From 1b1df6b6bcabedcf27ebc5c1cc4f2c0fac1e73b8 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Fri, 18 Dec 2020 23:51:05 +0530 Subject: [PATCH 306/728] feat: Add month_to_date field --- erpnext/payroll/doctype/salary_slip/salary_slip.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json index b64e5a08fe..5141868adb 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.json +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json @@ -73,6 +73,7 @@ "column_break_53", "rounded_total", "base_rounded_total", + "month_to_date", "section_break_55", "total_in_words", "column_break_69", @@ -583,14 +584,21 @@ { "fieldname": "year_to_date", "fieldtype": "Currency", - "label": "Year To Date" + "label": "Year To Date(Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "month_to_date", + "fieldtype": "Currency", + "label": "Month To Date" } ], "icon": "fa fa-file-text", "idx": 9, "is_submittable": 1, "links": [], - "modified": "2020-12-17 21:51:19.612940", + "modified": "2020-12-18 23:23:10.484574", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Slip", From 59fbf702dad1c4231d19fe8cfb4970862e4aaad0 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Fri, 18 Dec 2020 23:52:11 +0530 Subject: [PATCH 307/728] feat: Compute month_to_date --- .../doctype/salary_slip/salary_slip.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 0ea0684a8f..e86a7fc315 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -50,6 +50,7 @@ class SalarySlip(TransactionBase): self.calculate_net_pay() self.compute_year_to_date() + self.compute_month_to_date() 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") @@ -1138,8 +1139,7 @@ class SalarySlip(TransactionBase): fields = ['employee_name', 'start_date', 'end_date', 'net_pay'], filters = {'employee_name' : self.employee_name, 'start_date' : ['>=', payroll_period.start_date], - 'end_date' : ['<=', payroll_period.end_date], - 'name' : ['!=', self.name] + 'end_date' : ['<', self.start_date] }) for salary_slip in salary_slips_from_current_payroll_period: @@ -1148,6 +1148,22 @@ class SalarySlip(TransactionBase): year_to_date += self.net_pay self.year_to_date = year_to_date + def compute_month_to_date(self): + month_to_date = 0 + date = datetime.datetime.strptime(self.start_date,"%Y-%m-%d") + first_day_of_the_month = "1-" + str(date.month) + "-" + str(date.year) + salary_slips_from_this_month = frappe.get_list('Salary Slip', + fields = ['employee_name', 'start_date', 'net_pay'], + filters = {'employee_name' : self.employee_name, + 'start_date' : ['>=', first_day_of_the_month], + 'end_date' : ['<', self.start_date] + }) + for salary_slip in salary_slips_from_this_month: + month_to_date += salary_slip.net_pay + + month_to_date += self.net_pay + self.month_to_date = month_to_date + 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)) From ddd9fe49fca4f05f31bc97e462a478aa52477e2d Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Fri, 18 Dec 2020 23:58:05 +0530 Subject: [PATCH 308/728] feat: Add month_to_date field --- erpnext/payroll/doctype/salary_slip/salary_slip.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json index 5141868adb..d981a39953 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.json +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json @@ -591,14 +591,16 @@ { "fieldname": "month_to_date", "fieldtype": "Currency", - "label": "Month To Date" + "label": "Month To Date(Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 } ], "icon": "fa fa-file-text", "idx": 9, "is_submittable": 1, "links": [], - "modified": "2020-12-18 23:23:10.484574", + "modified": "2020-12-18 23:57:41.042954", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Slip", From 0c4f97368d05f2277ccca54b583be0a104acf7a9 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 21 Dec 2020 11:44:48 +0530 Subject: [PATCH 309/728] chore: UX improvement - Removed 'single reading' checkbox, unnecessary - Removed 'Mean' field and added computed mean to formula data - Changed 'Value Based' to 'Non-Numeric' - Re-arranged fields --- .../item_quality_inspection_parameter.json | 43 +++++------- .../quality_inspection/quality_inspection.py | 57 +++++++--------- .../quality_inspection_reading.json | 65 +++++-------------- .../quality_inspection_template.py | 3 +- 4 files changed, 58 insertions(+), 110 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 f450128157..9b980a1e01 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 @@ -8,14 +8,12 @@ "field_order": [ "specification", "value", - "value_based", - "single_reading", + "non_numeric", "column_break_3", - "formula_based_criteria", - "acceptance_formula", "min_value", "max_value", - "mean_value" + "formula_based_criteria", + "acceptance_formula" ], "fields": [ { @@ -27,10 +25,10 @@ "oldfieldtype": "Data", "print_width": "200px", "reqd": 1, - "width": "200px" + "width": "100px" }, { - "depends_on": "eval:(!doc.formula_based_criteria && doc.value_based)", + "depends_on": "eval:(!doc.formula_based_criteria && doc.non_numeric)", "fieldname": "value", "fieldtype": "Data", "in_list_view": 1, @@ -44,10 +42,9 @@ }, { "depends_on": "formula_based_criteria", - "description": "Simple Python formula applied on Reading fields.
    Numeric eg.: reading_1 > 0.2 and reading_1 < 0.5
    \nValue based eg.: reading_value in (\"A\", \"B\", \"C)", + "description": "Simple Python formula applied on Reading fields.
    Numeric eg. 1: reading_1 > 0.2 and reading_1 < 0.5
    \nNumeric eg. 2: mean > 3.5 (mean of populated fields)
    \nValue based eg.: reading_value in (\"A\", \"B\", \"C)", "fieldname": "acceptance_formula", "fieldtype": "Code", - "in_list_view": 1, "label": "Acceptance Criteria Formula" }, { @@ -57,42 +54,32 @@ "label": "Formula Based Criteria" }, { - "default": "0", - "depends_on": "eval:!doc.value_based", - "fieldname": "single_reading", - "fieldtype": "Check", - "label": "Single Reading" - }, - { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.single_reading && !doc.value_based)", - "fieldname": "mean_value", - "fieldtype": "Float", - "label": "Mean Value" - }, - { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "depends_on": "eval:(!doc.formula_based_criteria && !doc.non_numeric)", "fieldname": "min_value", "fieldtype": "Float", + "in_list_view": 1, "label": "Minimum Value" }, { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "depends_on": "eval:(!doc.formula_based_criteria && !doc.non_numeric)", "fieldname": "max_value", "fieldtype": "Float", + "in_list_view": 1, "label": "Maximum Value" }, { "default": "0", - "description": "Non-numeric Inspection.", - "fieldname": "value_based", + "fieldname": "non_numeric", "fieldtype": "Check", - "label": "Value Based" + "in_list_view": 1, + "label": "Non-Numeric", + "width": "80px" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-18 21:03:29.828723", + "modified": "2020-12-21 11:37:55.387677", "modified_by": "Administrator", "module": "Stock", "name": "Item Quality Inspection Parameter", diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index a7a023bcbf..f582658d87 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -79,46 +79,27 @@ class QualityInspection(Document): if reading.formula_based_criteria: self.set_status_based_on_acceptance_formula(reading) else: + # if not formula based check acceptance values set self.set_status_based_on_acceptance_values(reading) def set_status_based_on_acceptance_values(self, reading): - if cint(reading.value_based): + if cint(reading.non_numeric): result = reading.get("reading_value") == reading.get("value") else: # numeric readings - if cint(reading.single_reading): - reading_1 = flt(reading.get("reading_1")) - result = flt(reading.get("min_value")) <= reading_1 <= flt(reading.get("max_value")) - else: - result = self.min_max_criteria_passed(reading) and self.mean_criteria_passed(reading) + result = self.min_max_criteria_passed(reading) reading.status = "Accepted" if result else "Rejected" def min_max_criteria_passed(self, reading): """Determine whether all readings fall in the acceptable range.""" for i in range(1, 11): - reading_field = reading.get("reading_" + str(i)) - if reading_field is not None: - result = flt(reading.get("min_value")) <= flt(reading_field) <= flt(reading.get("max_value")) + reading_value = reading.get("reading_" + str(i)) + if reading_value is not None and reading_value.strip(): + result = flt(reading.get("min_value")) <= flt(reading_value) <= flt(reading.get("max_value")) if not result: return False return True - def mean_criteria_passed(self, reading): - """Determine whether mean of all readings is acceptable.""" - if reading.get("mean_value"): - from statistics import mean - readings_list = [] - - for i in range(1, 11): - reading_value = reading.get("reading_" + str(i)) - if reading_value is not None: - readings_list.append(flt(reading_value)) - - actual_mean = mean(readings_list) if readings_list else 0 - return True if actual_mean == reading.get("mean_value") else False - - return True # no mean value, nothing to check - def set_status_based_on_acceptance_formula(self, reading): if not reading.acceptance_formula: frappe.throw(_("Row #{0}: Acceptance Criteria Formula is required.").format(reading.idx), @@ -141,18 +122,30 @@ class QualityInspection(Document): def get_formula_evaluation_data(self, reading): data = {} - if cint(reading.value_based): + if cint(reading.non_numeric): data = {"reading_value": reading.get("reading_value")} else: # numeric readings - data = {"reading_1": flt(reading.get("reading_1"))} - if not cint(reading.single_reading): - # if multiple numeric readings add all readings to data - for i in range(2, 11): - field = "reading_" + str(i) - data[field] = flt(reading.get(field)) + for i in range(1, 11): + field = "reading_" + str(i) + data[field] = flt(reading.get(field)) + data["mean"] = self.calculate_mean(reading) + return data + def calculate_mean(self, reading): + """Calculate mean of all non-empty readings.""" + from statistics import mean + readings_list = [] + + for i in range(1, 11): + reading_value = reading.get("reading_" + str(i)) + if reading_value is not None and reading_value.strip(): + readings_list.append(flt(reading_value)) + + actual_mean = mean(readings_list) if readings_list else 0 + return actual_mean + @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_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json index db95fabee0..0792f26d2a 100644 --- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json +++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json @@ -9,18 +9,15 @@ "specification", "status", "value", - "value_based", + "non_numeric", "column_break_4", - "formula_based_criteria", - "acceptance_formula", "min_value", "max_value", - "mean_value", + "formula_based_criteria", + "acceptance_formula", "section_break_3", "reading_value", "section_break_14", - "single_reading", - "section_break_12", "reading_1", "reading_2", "reading_3", @@ -47,7 +44,7 @@ }, { "columns": 2, - "depends_on": "eval:(!doc.formula_based_criteria && doc.value_based)", + "depends_on": "eval:(!doc.formula_based_criteria && doc.non_numeric)", "fieldname": "value", "fieldtype": "Data", "in_list_view": 1, @@ -66,7 +63,6 @@ }, { "columns": 1, - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_2", "fieldtype": "Data", "in_list_view": 1, @@ -76,7 +72,6 @@ }, { "columns": 1, - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_3", "fieldtype": "Data", "in_list_view": 1, @@ -85,7 +80,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_4", "fieldtype": "Data", "label": "Reading 4", @@ -93,7 +87,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_5", "fieldtype": "Data", "label": "Reading 5", @@ -101,7 +94,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_6", "fieldtype": "Data", "label": "Reading 6", @@ -109,7 +101,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_7", "fieldtype": "Data", "label": "Reading 7", @@ -117,7 +108,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_8", "fieldtype": "Data", "label": "Reading 8", @@ -125,7 +115,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_9", "fieldtype": "Data", "label": "Reading 9", @@ -133,7 +122,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_10", "fieldtype": "Data", "label": "Reading 10", @@ -152,7 +140,7 @@ "options": "Accepted\nRejected" }, { - "depends_on": "value_based", + "depends_on": "non_numeric", "fieldname": "section_break_3", "fieldtype": "Section Break", "label": "Value Based Inspection" @@ -163,7 +151,7 @@ }, { "depends_on": "formula_based_criteria", - "description": "Simple Python formula applied on Reading fields.
    Numeric eg.: reading_1 > 0.2 and reading_1 < 0.5
    \nValue based eg.: reading_value in (\"A\", \"B\", \"C)", + "description": "Simple Python formula applied on Reading fields.
    Numeric eg. 1: reading_1 > 0.2 and reading_1 < 0.5
    \nNumeric eg. 2: mean > 3.5 (mean of populated fields)
    \nValue based eg.: reading_value in (\"A\", \"B\", \"C)", "fieldname": "acceptance_formula", "fieldtype": "Code", "label": "Acceptance Criteria Formula" @@ -183,61 +171,42 @@ "label": "Formula Based Criteria" }, { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.single_reading && !doc.value_based)", - "fieldname": "mean_value", - "fieldtype": "Float", - "label": "Mean Value" - }, - { - "default": "0", - "fieldname": "single_reading", - "fieldtype": "Check", - "label": "Single Reading" - }, - { - "depends_on": "eval:!doc.value_based", - "fieldname": "section_break_12", - "fieldtype": "Section Break", - "hide_border": 1 - }, - { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "depends_on": "eval:(!doc.formula_based_criteria && !doc.non_numeric)", "description": "Applied on each reading.", "fieldname": "min_value", "fieldtype": "Float", "label": "Minimum Value" }, { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "depends_on": "eval:(!doc.formula_based_criteria && !doc.non_numeric)", "description": "Applied on each reading.", "fieldname": "max_value", "fieldtype": "Float", "label": "Maximum Value" }, { - "default": "0", - "description": "Non-numeric Inspection.", - "fieldname": "value_based", - "fieldtype": "Check", - "label": "Value Based" - }, - { - "depends_on": "value_based", + "depends_on": "non_numeric", "fieldname": "reading_value", "fieldtype": "Data", "label": "Reading Value" }, { - "depends_on": "eval:!doc.value_based", + "depends_on": "eval:!doc.non_numeric", "fieldname": "section_break_14", "fieldtype": "Section Break", "label": "Numeric Inspection" + }, + { + "default": "0", + "fieldname": "non_numeric", + "fieldtype": "Check", + "label": "Non-Numeric" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-18 21:02:04.865777", + "modified": "2020-12-21 11:36:24.885019", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection Reading", 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 7dd0febc20..c5a7974a73 100644 --- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py +++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py @@ -14,7 +14,6 @@ def get_template_details(template): return frappe.get_all('Item Quality Inspection Parameter', fields=["specification", "value", "acceptance_formula", - "value_based", "formula_based_criteria", "single_reading", - "min_value", "max_value", "mean_value"], + "non_numeric", "formula_based_criteria", "min_value", "max_value"], filters={'parenttype': 'Quality Inspection Template', 'parent': template}, order_by="idx") \ No newline at end of file From 68f91c96400226254875016d0e0b95bdc3816580 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 21 Dec 2020 12:24:45 +0530 Subject: [PATCH 310/728] chore: Added tests for new ux - Test for value based inspection - tweaks in test for formula based inspection - tweaks in create_quality_inspection as status in child row is auto set now --- .../test_quality_inspection.py | 59 ++++++++++++++++--- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py index 2c40009426..d0bfb466e0 100644 --- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py @@ -44,24 +44,61 @@ class TestQualityInspection(unittest.TestCase): qa.delete() dn.delete() + def test_value_based_qi_readings(self): + # Test QI based on acceptance values (Non formula) + dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True) + readings = [{ + "specification": "Iron Content", # numeric reading + "min_value": 0.1, + "max_value": 0.9, + "reading_1": "0.4" + }, + { + "specification": "Particle Inspection Needed", # non-numeric reading + "non_numeric": 1, + "value": "Yes", + "reading_value": "Yes" + }] + + 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, "Accepted") + + 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", + "specification": "Iron Content", # numeric reading + "formula_based_criteria": 1, "acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50", - "reading_1": 0.4 + "reading_1": "0.4" }, { - "specification": "Calcium Content", + "specification": "Calcium Content", # numeric reading + "formula_based_criteria": 1, "acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50", - "reading_1": 0.7 + "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, + "specification": "Mg Content", # numeric reading + "formula_based_criteria": 1, + "acceptance_formula": "mean < 0.9", + "reading_1": "0.5", + "reading_2": "0.7", "reading_3": "random text" # check if random string input causes issues + }, + { + "specification": "Calcium Content", # non-numeric reading + "formula_based_criteria": 1, + "non_numeric": 1, + "acceptance_formula": "reading_value in ('Grade A', 'Grade B', 'Grade C')", + "reading_value": "Grade B" }] qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, @@ -72,6 +109,7 @@ class TestQualityInspection(unittest.TestCase): self.assertEqual(qa.readings[0].status, "Accepted") self.assertEqual(qa.readings[1].status, "Rejected") self.assertEqual(qa.readings[2].status, "Accepted") + self.assertEqual(qa.readings[3].status, "Accepted") qa.delete() dn.delete() @@ -86,8 +124,11 @@ 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.status = args.status or "Accepted" - readings = args.readings or {"specification": "Size", "status": args.status} + readings = args.readings or {"specification": "Size", "min_value": 0, "max_value": 10} + if args.status == "Rejected": + readings["reading_1"] = "12" # status is auto set in child on save if isinstance(readings, list): for entry in readings: From 0e222173ea431b46344f7b73866390c15e80106e Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 21 Dec 2020 13:44:03 +0530 Subject: [PATCH 311/728] fix: don't set primary action if workflow is set --- erpnext/payroll/doctype/payroll_entry/payroll_entry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js index cb48abbc36..31abaf40bf 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js @@ -39,7 +39,7 @@ frappe.ui.form.on('Payroll Entry', { } ).toggleClass('btn-primary', !(frm.doc.employees || []).length); } - if ((frm.doc.employees || []).length) { + if ((frm.doc.employees || []).length && !frappe.model.has_workflow(frm.doctype)) { frm.page.clear_primary_action(); frm.page.set_primary_action(__('Create Salary Slips'), () => { frm.save('Submit').then(()=>{ From eae31f02cc1a5254292b7c621513e70b91d10b22 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 21 Dec 2020 13:58:44 +0530 Subject: [PATCH 312/728] fix: Sider (missing semi-colons) --- .../doctype/quality_inspection/quality_inspection.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.js b/erpnext/stock/doctype/quality_inspection/quality_inspection.js index f0bf9aed80..2ec8a07005 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.js +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.js @@ -10,18 +10,18 @@ frappe.ui.form.on("Quality Inspection", { filters: { "item": frm.doc.item_code } - } + }; }); // Serial No based on item_code frm.set_query("item_serial_no", function() { - var filters = {}; + let filters = {}; if (frm.doc.item_code) { filters = { 'item_code': frm.doc.item_code - } + }; } - return { filters: filters } + return { filters: filters }; }); // item code based on GRN/DN @@ -75,4 +75,4 @@ frappe.ui.form.on("Quality Inspection", { }); } }, -}) \ No newline at end of file +}); \ No newline at end of file From a77b8c9fcc1ed7a2a44d2cdf3a3b50c5d8a4366f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 21 Dec 2020 14:45:50 +0530 Subject: [PATCH 313/728] Repost item valuation (#24031) * feat: Reposting logic for future finished/transferred item * feat: added fields to identify needs to recalculate rate while reposting * refactor: Set rate for outgoing and finished items * refactor: Arranged fields in Stock Entry item table and added fields to identify finished and scrap item * refactor: Arranged fields in Stock Entry item table and added fields to identify finished and scrap item * refactor: Get outgoing rate for purchase return * refactor: Get incoming rate for sales return * test: Added tests for reposting valuation of transferred/finished/returned items * feat: added incoming rate field in DN, SI and Packed Item table * feat: get incoming rate for returned item * fix: no error while getting valuation rate in stock entry * fix: update stock ledger for DN and SI * feat: update item valuation rate in PR and PI based on supplied items cost * feat: SLE reposting logic for sales return and subcontracted item with test cases * feat: update qty in future sle * feat: repost future sle and gle via Repost Item Valuation * fix: Skip unwanted function calling while reposting * fix: repost sle for specific item and warehouse * test: Modified tests for backdated stock reco * fix: ignore cancelled sle in few methods * feat: role allowed to do backdated entry * feat: Show reposting status on stock valuation related reports * fix: minor fixes * fix: fixed sider issues * fix: serial no fix related to immutable ledger * fix: Test cases fixes related to perpetual inventory * fix: Test cases fixed * fix: Fixed reposting on cancel and test cases * feat: Restart reposting item valuation * refactor: Code cleanup using small functions and test case fixes * fix: minor fixes * fix: Raise on error while reposting item valuation * fix: minor fix * fix: Tests fixed * fix: skip some validation ig gle made from reposting * fix: test fixes * fix: debugging stock and account validation * fix: debugging stock and account validation * fix: debugging travis for stock and account sync validation * fix: debugging travis * fix: debugging travis * fix: debugging travis --- .../accounts/doctype/account/test_account.py | 2 +- .../doctype/coupon_code/test_coupon_code.py | 50 +- erpnext/accounts/doctype/gl_entry/gl_entry.py | 24 +- .../journal_entry/test_journal_entry.py | 68 +- .../loyalty_program/test_loyalty_program.py | 2 - .../purchase_invoice/purchase_invoice.py | 19 +- .../purchase_invoice/test_purchase_invoice.py | 72 +-- .../doctype/sales_invoice/sales_invoice.py | 17 +- .../doctype/sales_invoice/test_records.json | 3 +- .../sales_invoice/test_sales_invoice.py | 128 ++-- .../sales_invoice_item.json | 30 +- erpnext/accounts/general_ledger.py | 38 +- erpnext/accounts/utils.py | 24 +- .../purchase_order_item.json | 2 +- erpnext/controllers/buying_controller.py | 129 ++-- .../controllers/sales_and_purchase_return.py | 42 ++ erpnext/controllers/selling_controller.py | 116 ++-- erpnext/controllers/stock_controller.py | 101 +-- .../doctype/work_order/test_work_order.py | 2 - .../sales_order_item/sales_order_item.json | 2 +- .../setup/doctype/company/test_records.json | 18 +- erpnext/stock/doctype/batch/test_batch.py | 5 - erpnext/stock/doctype/bin/bin.py | 14 +- .../doctype/delivery_note/delivery_note.py | 4 +- .../delivery_note/test_delivery_note.py | 7 +- .../delivery_note_item.json | 11 +- erpnext/stock/doctype/item/test_records.json | 10 + .../item_alternative/test_item_alternative.py | 2 - .../landed_cost_taxes_and_charges.json | 8 +- .../landed_cost_voucher.py | 18 +- .../test_landed_cost_voucher.py | 12 +- .../material_request/test_material_request.py | 3 - .../doctype/packed_item/packed_item.json | 18 +- .../purchase_receipt/purchase_receipt.py | 4 +- .../purchase_receipt/test_purchase_receipt.py | 188 +++--- .../purchase_receipt_item.json | 2 +- .../doctype/repost_item_valuation/__init__.py | 0 .../repost_item_valuation.js | 52 ++ .../repost_item_valuation.json | 215 +++++++ .../repost_item_valuation.py | 89 +++ .../test_repost_item_valuation.py | 10 + erpnext/stock/doctype/serial_no/serial_no.py | 33 +- .../stock/doctype/serial_no/test_serial_no.py | 3 - .../stock/doctype/stock_entry/stock_entry.js | 25 +- .../doctype/stock_entry/stock_entry.json | 3 +- .../stock/doctype/stock_entry/stock_entry.py | 303 +++++---- .../doctype/stock_entry/test_stock_entry.py | 83 +-- .../stock_entry_detail.json | 74 ++- .../stock_ledger_entry.json | 62 +- .../stock_ledger_entry/stock_ledger_entry.py | 45 +- .../test_stock_ledger_entry.py | 395 +++++++++++- .../stock_reconciliation.py | 4 +- .../test_stock_reconciliation.py | 47 +- .../stock_settings/stock_settings.json | 31 +- .../stock/doctype/warehouse/test_warehouse.py | 61 +- erpnext/stock/doctype/warehouse/warehouse.py | 1 - .../report/stock_analytics/stock_analytics.py | 2 + .../report/stock_balance/stock_balance.py | 3 +- .../stock/report/stock_ledger/stock_ledger.py | 3 +- .../stock_projected_qty.py | 3 +- ...rehouse_wise_item_balance_age_and_value.py | 2 + erpnext/stock/stock_balance.py | 18 +- erpnext/stock/stock_ledger.py | 597 +++++++++++++----- erpnext/stock/utils.py | 11 +- 64 files changed, 2336 insertions(+), 1034 deletions(-) create mode 100644 erpnext/stock/doctype/repost_item_valuation/__init__.py create mode 100644 erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js create mode 100644 erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json create mode 100644 erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py create mode 100644 erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index 0605d89a7e..113bea0064 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -172,7 +172,7 @@ class TestAccount(unittest.TestCase): frappe.delete_doc("Account", doc) -def _make_test_records(verbose): +def _make_test_records(verbose=None): from frappe.test_runner import make_test_objects accounts = [ diff --git a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py index 340b9dd58a..622bd33e20 100644 --- a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py +++ b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py @@ -28,22 +28,22 @@ def test_create_test_data(): "item_group": "_Test Item Group", "item_name": "_Test Tesla Car", "apply_warehouse_wise_reorder_level": 0, - "warehouse":"Stores - TCP1", + "warehouse":"Stores - _TC", "gst_hsn_code": "999800", "valuation_rate": 5000, "standard_rate":5000, "item_defaults": [{ - "company": "_Test Company with perpetual inventory", - "default_warehouse": "Stores - TCP1", + "company": "_Test Company", + "default_warehouse": "Stores - _TC", "default_price_list":"_Test Price List", - "expense_account": "Cost of Goods Sold - TCP1", - "buying_cost_center": "Main - TCP1", - "selling_cost_center": "Main - TCP1", - "income_account": "Sales - TCP1" + "expense_account": "Cost of Goods Sold - _TC", + "buying_cost_center": "Main - _TC", + "selling_cost_center": "Main - _TC", + "income_account": "Sales - _TC" }], "show_in_website": 1, "route":"-test-tesla-car", - "website_warehouse": "Stores - TCP1" + "website_warehouse": "Stores - _TC" }) item.insert() # create test item price @@ -65,12 +65,12 @@ def test_create_test_data(): "items": [{ "item_code": "_Test Tesla Car" }], - "warehouse":"Stores - TCP1", + "warehouse":"Stores - _TC", "coupon_code_based":1, "selling": 1, "rate_or_discount": "Discount Percentage", "discount_percentage": 30, - "company": "_Test Company with perpetual inventory", + "company": "_Test Company", "currency":"INR", "for_price_list":"_Test Price List" }) @@ -85,7 +85,7 @@ def test_create_test_data(): }) sales_partner.insert() # create test item coupon code - if not frappe.db.exists("Coupon Code","SAVE30"): + if not frappe.db.exists("Coupon Code", "SAVE30"): coupon_code = frappe.get_doc({ "doctype": "Coupon Code", "coupon_name":"SAVE30", @@ -102,35 +102,27 @@ class TestCouponCode(unittest.TestCase): test_create_test_data() def tearDown(self): - frappe.set_user("Administrator") + frappe.set_user("Administrator") - def test_1_check_coupon_code_used_before_so(self): - coupon_code = frappe.get_doc("Coupon Code", frappe.db.get_value("Coupon Code", {"coupon_name":"SAVE30"})) - # reset used coupon code count - coupon_code.used=0 - coupon_code.save() - # check no coupon code is used before sales order is made - self.assertEqual(coupon_code.get("used"),0) + def test_sales_order_with_coupon_code(self): + frappe.db.set_value("Coupon Code", "SAVE30", "used", 0) - def test_2_sales_order_with_coupon_code(self): - so = make_sales_order(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', - customer="_Test Customer", selling_price_list="_Test Price List", item_code="_Test Tesla Car", rate=5000,qty=1, + so = make_sales_order(company='_Test Company', warehouse='Stores - _TC', + customer="_Test Customer", selling_price_list="_Test Price List", + item_code="_Test Tesla Car", rate=5000, qty=1, do_not_submit=True) - so = frappe.get_doc('Sales Order', so.name) - # check item price before coupon code is applied self.assertEqual(so.items[0].rate, 5000) + so.coupon_code='SAVE30' so.sales_partner='_Test Coupon Partner' so.save() + # check item price after coupon code is applied self.assertEqual(so.items[0].rate, 3500) + so.submit() - - def test_3_check_coupon_code_used_after_so(self): - doc = frappe.get_doc("Coupon Code", frappe.db.get_value("Coupon Code", {"coupon_name":"SAVE30"})) - # check no coupon code is used before sales order is made - self.assertEqual(doc.get("used"),1) + self.assertEqual(frappe.db.get_value("Coupon Code", "SAVE30", "used"), 1) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index def9ed6803..c441274908 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -30,20 +30,22 @@ class GLEntry(Document): self.pl_must_have_cost_center() self.validate_cost_center() - self.check_pl_account() - self.validate_party() - self.validate_currency() + if not self.flags.from_repost: + self.check_pl_account() + self.validate_party() + self.validate_currency() - def on_update_with_args(self, adv_adj, update_outstanding = 'Yes'): - self.validate_account_details(adv_adj) - self.validate_dimensions_for_pl_and_bs() + def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False): + if not from_repost: + self.validate_account_details(adv_adj) + self.validate_dimensions_for_pl_and_bs() validate_frozen_account(self.account, adv_adj) validate_balance_type(self.account, adv_adj) # Update outstanding amt on against voucher if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] \ - and self.against_voucher and update_outstanding == 'Yes': + and self.against_voucher and update_outstanding == 'Yes' and not from_repost: update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type, self.against_voucher) @@ -106,8 +108,8 @@ class GLEntry(Document): from tabAccount where name=%s""", self.account, as_dict=1)[0] if ret.is_group==1: - frappe.throw(_('''{0} {1}: Account {2} is a Group Account and group accounts cannot be used in - transactions''').format(self.voucher_type, self.voucher_no, self.account)) + frappe.throw(_('''{0} {1}: Account {2} is a Group Account and group accounts cannot be used in transactions''') + .format(self.voucher_type, self.voucher_no, self.account)) if ret.docstatus==2: frappe.throw(_("{0} {1}: Account {2} is inactive") @@ -136,8 +138,8 @@ class GLEntry(Document): .format(self.voucher_type, self.voucher_no, self.cost_center, self.company)) if self.cost_center and _check_is_group(): - frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot - be used in transactions""").format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center))) + frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""") + .format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center))) def validate_party(self): validate_party_frozen_disabled(self.party_type, self.party) diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 53c07583d8..1d2eacdb80 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -75,54 +75,40 @@ class TestJournalEntry(unittest.TestCase): elif test_voucher.doctype in ["Sales Order", "Purchase Order"]: # if test_voucher is a Sales Order/Purchase Order, test error on cancellation of test_voucher + frappe.db.set_value("Accounts Settings", "Accounts Settings", + "unlink_advance_payment_on_cancelation_of_order", 0) submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name) self.assertRaises(frappe.LinkExistsError, submitted_voucher.cancel) def test_jv_against_stock_account(self): - from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory - set_perpetual_inventory() + company = "_Test Company with perpetual inventory" + stock_account = get_inventory_account(company) - jv = frappe.copy_doc({ - "cheque_date": nowdate(), - "cheque_no": "33", - "company": "_Test Company with perpetual inventory", - "doctype": "Journal Entry", - "accounts": [ - { - "account": "Debtors - TCP1", - "party_type": "Customer", - "party": "_Test Customer", - "credit_in_account_currency": 400.0, - "debit_in_account_currency": 0.0, - "doctype": "Journal Entry Account", - "parentfield": "accounts", - "cost_center": "Main - TCP1" - }, - { - "account": "_Test Bank - TCP1", - "credit_in_account_currency": 0.0, - "debit_in_account_currency": 400.0, - "doctype": "Journal Entry Account", - "parentfield": "accounts", - "cost_center": "Main - TCP1" - } - ], - "naming_series": "_T-Journal Entry-", - "posting_date": nowdate(), - "user_remark": "test", - "voucher_type": "Bank Entry" - }) - - jv.get("accounts")[0].update({ - "account": get_inventory_account('_Test Company with perpetual inventory'), - "company": "_Test Company with perpetual inventory", - "party_type": None, - "party": None + jv = frappe.new_doc("Journal Entry") + jv.company = company + jv.posting_date = nowdate() + jv.append("accounts", { + "account": stock_account, + "cost_center": "Main - TCP1", + "debit_in_account_currency": 100 }) + + jv.append("accounts", { + "account": "Stock Adjustment - TCP1", + "credit_in_account_currency": 100, + "cost_center": "Main - TCP1", + }) + jv.insert() - self.assertRaises(StockAccountInvalidTransaction, jv.submit) - jv.cancel() - set_perpetual_inventory(0) + from erpnext.accounts.utils import get_stock_and_account_balance + account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(stock_account, nowdate(), company) + + if account_bal == stock_bal: + self.assertRaises(StockAccountInvalidTransaction, jv.submit) + frappe.db.rollback() + else: + jv.submit() + jv.cancel() def test_multi_currency(self): jv = make_journal_entry("_Test Bank USD - _TC", diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py index 5278d8b241..31994885aa 100644 --- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py @@ -8,12 +8,10 @@ import unittest from frappe.utils import today, cint, flt, getdate from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points from erpnext.accounts.party import get_dashboard_info -from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory class TestLoyaltyProgram(unittest.TestCase): @classmethod def setUpClass(self): - set_perpetual_inventory(0) # create relevant item, customer, loyalty program, etc create_records() diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index d94d261c6b..b52678e8d3 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -410,10 +410,13 @@ class PurchaseInvoice(BuyingController): # this sequence because outstanding may get -negative self.make_gl_entries() + if self.update_stock == 1: + self.repost_future_sle_and_gle() + self.update_project() update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference) - def make_gl_entries(self, gl_entries=None): + def make_gl_entries(self, gl_entries=None, from_repost=False): if not gl_entries: gl_entries = self.get_gl_entries() @@ -421,7 +424,7 @@ class PurchaseInvoice(BuyingController): update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes" if self.docstatus == 1: - make_gl_entries(gl_entries, update_outstanding=update_outstanding, merge_entries=False) + make_gl_entries(gl_entries, update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost) elif self.docstatus == 2: make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) @@ -436,9 +439,11 @@ class PurchaseInvoice(BuyingController): self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company) if self.auto_accounting_for_stock: self.stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed") + self.expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") else: self.stock_received_but_not_billed = None - self.expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") + self.expenses_included_in_valuation = None + self.negative_expense_to_be_booked = 0.0 gl_entries = [] @@ -452,7 +457,7 @@ class PurchaseInvoice(BuyingController): self.make_internal_transfer_gl_entries(gl_entries) gl_entries = make_regional_gl_entries(gl_entries, self) - + gl_entries = merge_similar_entries(gl_entries) self.make_payment_gl_entries(gl_entries) @@ -994,11 +999,15 @@ class PurchaseInvoice(BuyingController): self.delete_auto_created_batches() self.make_gl_entries_on_cancel() + + if self.update_stock == 1: + self.repost_future_sle_and_gle() + self.update_project() frappe.db.set(self, 'status', 'Cancelled') unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference) - self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') + self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation') def update_project(self): project_list = [] diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index f2499d24b5..c0506ba97f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -9,8 +9,7 @@ import frappe.model from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from frappe.utils import cint, flt, today, nowdate, add_days, getdate import frappe.defaults -from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \ - test_records as pr_test_records, make_purchase_receipt, get_taxes +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt, get_taxes from erpnext.controllers.accounts_controller import get_payment_terms from erpnext.exceptions import InvalidCurrency from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction @@ -33,13 +32,10 @@ class TestPurchaseInvoice(unittest.TestCase): def test_gl_entries_without_perpetual_inventory(self): frappe.db.set_value("Company", "_Test Company", "round_off_account", "Round Off - _TC") - wrapper = frappe.copy_doc(test_records[0]) - set_perpetual_inventory(0, wrapper.company) - self.assertTrue(not cint(erpnext.is_perpetual_inventory_enabled(wrapper.company))) - wrapper.insert() - wrapper.submit() - wrapper.load_from_db() - dl = wrapper + pi = frappe.copy_doc(test_records[0]) + self.assertTrue(not cint(erpnext.is_perpetual_inventory_enabled(pi.company))) + pi.insert() + pi.submit() expected_gl_entries = { "_Test Payable - _TC": [0, 1512.0], @@ -54,12 +50,16 @@ class TestPurchaseInvoice(unittest.TestCase): "Round Off - _TC": [0, 0.3] } gl_entries = frappe.db.sql("""select account, debit, credit from `tabGL Entry` - where voucher_type = 'Purchase Invoice' and voucher_no = %s""", dl.name, as_dict=1) + where voucher_type = 'Purchase Invoice' and voucher_no = %s""", pi.name, as_dict=1) for d in gl_entries: self.assertEqual([d.debit, d.credit], expected_gl_entries.get(d.account)) def test_gl_entries_with_perpetual_inventory(self): - pi = make_purchase_invoice(company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1", get_taxes_and_charges=True, qty=10) + pi = make_purchase_invoice(company="_Test Company with perpetual inventory", + warehouse= "Stores - TCP1", cost_center = "Main - TCP1", + expense_account ="_Test Account Cost for Goods Sold - TCP1", + get_taxes_and_charges=True, qty=10) + self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pi.company)), 1) self.check_gle_for_pi(pi.name) @@ -198,8 +198,6 @@ class TestPurchaseInvoice(unittest.TestCase): pr = make_purchase_receipt(company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", get_taxes_and_charges=True,) - self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pr.company)), 1) - pi = make_purchase_invoice(company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1", get_taxes_and_charges=True, qty=10,do_not_save= "True") for d in pi.items: @@ -247,17 +245,11 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertRaises(frappe.CannotChangeConstantError, pi.save) - def test_gl_entries_with_aia_for_non_stock_items(self): - pi = frappe.copy_doc(test_records[1]) - set_perpetual_inventory(1, pi.company) - self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pi.company)), 1) - pi.get("items")[0].item_code = "_Test Non Stock Item" - pi.get("items")[0].expense_account = "_Test Account Cost for Goods Sold - _TC" - pi.get("taxes").pop(0) - pi.get("taxes").pop(1) - pi.insert() - pi.submit() - pi.load_from_db() + def test_gl_entries_for_non_stock_items_with_perpetual_inventory(self): + pi = make_purchase_invoice(item_code = "_Test Non Stock Item", + company = "_Test Company with perpetual inventory", warehouse= "Stores - TCP1", + cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1") + self.assertTrue(pi.status, "Unpaid") gl_entries = frappe.db.sql("""select account, debit, credit @@ -265,17 +257,15 @@ class TestPurchaseInvoice(unittest.TestCase): order by account asc""", pi.name, as_dict=1) self.assertTrue(gl_entries) - expected_values = sorted([ - ["_Test Payable - _TC", 0, 620], - ["_Test Account Cost for Goods Sold - _TC", 500.0, 0], - ["_Test Account VAT - _TC", 120.0, 0], - ]) + expected_values = [ + ["_Test Account Cost for Goods Sold - TCP1", 250.0, 0], + ["Creditors - TCP1", 0, 250] + ] for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[i][0], gle.account) self.assertEqual(expected_values[i][1], gle.debit) self.assertEqual(expected_values[i][2], gle.credit) - set_perpetual_inventory(0, pi.company) def test_purchase_invoice_calculation(self): pi = frappe.copy_doc(test_records[0]) @@ -457,12 +447,13 @@ class TestPurchaseInvoice(unittest.TestCase): pi.cancel() self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), existing_purchase_cost) - def test_return_purchase_invoice(self): - set_perpetual_inventory() + def test_return_purchase_invoice_with_perpetual_inventory(self): + pi = make_purchase_invoice(company = "_Test Company with perpetual inventory", warehouse= "Stores - TCP1", + cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1") - pi = make_purchase_invoice() - - return_pi = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2) + return_pi = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, + company = "_Test Company with perpetual inventory", warehouse= "Stores - TCP1", + cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1") # check gl entries for return @@ -473,19 +464,15 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertTrue(gl_entries) expected_values = { - "Creditors - _TC": [100.0, 0.0], - "Stock Received But Not Billed - _TC": [0.0, 100.0], + "Creditors - TCP1": [100.0, 0.0], + "Stock Received But Not Billed - TCP1": [0.0, 100.0], } for gle in gl_entries: self.assertEqual(expected_values[gle.account][0], gle.debit) self.assertEqual(expected_values[gle.account][1], gle.credit) - set_perpetual_inventory(0) - def test_multi_currency_gle(self): - set_perpetual_inventory(0) - pi = make_purchase_invoice(supplier="_Test Supplier USD", credit_to="_Test Payable USD - _TC", currency="USD", conversion_rate=50) @@ -640,10 +627,9 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(len(pi.get("supplied_items")), 2) rm_supp_cost = sum([d.amount for d in pi.get("supplied_items")]) - self.assertEqual(pi.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2)) + self.assertEqual(flt(pi.get("items")[0].rm_supp_cost, 2), flt(rm_supp_cost, 2)) def test_rejected_serial_no(self): - set_perpetual_inventory(0) pi = make_purchase_invoice(item_code="_Test Serialized Item With Series", received_qty=2, qty=1, rejected_qty=1, rate=500, update_stock=1, rejected_warehouse = "_Test Rejected Warehouse - _TC") diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index ca6f22cc30..50734c865c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -179,6 +179,9 @@ class SalesInvoice(SellingController): # this sequence because outstanding may get -ve self.make_gl_entries() + + if self.update_stock == 1: + self.repost_future_sle_and_gle() if not self.is_return: self.update_billing_status_for_zero_amount_refdoc("Delivery Note") @@ -258,6 +261,10 @@ class SalesInvoice(SellingController): self.update_stock_ledger() self.make_gl_entries_on_cancel() + + if self.update_stock == 1: + self.repost_future_sle_and_gle() + frappe.db.set(self, 'status', 'Cancelled') if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') == "Each Transaction": @@ -279,7 +286,7 @@ class SalesInvoice(SellingController): if "Healthcare" in active_domains: manage_invoice_submit_cancel(self, "on_cancel") - self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') + self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation') def update_status_updater_args(self): if cint(self.update_stock): @@ -722,22 +729,20 @@ class SalesInvoice(SellingController): if d.delivery_note and frappe.db.get_value("Delivery Note", d.delivery_note, "docstatus") != 1: throw(_("Delivery Note {0} is not submitted").format(d.delivery_note)) - def make_gl_entries(self, gl_entries=None): - from erpnext.accounts.general_ledger import make_reverse_gl_entries + def make_gl_entries(self, gl_entries=None, from_repost=False): + from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company) if not gl_entries: gl_entries = self.get_gl_entries() if gl_entries: - from erpnext.accounts.general_ledger import make_gl_entries - # if POS and amount is written off, updating outstanding amt after posting all gl entries update_outstanding = "No" if (cint(self.is_pos) or self.write_off_account or cint(self.redeem_loyalty_points)) else "Yes" if self.docstatus == 1: - make_gl_entries(gl_entries, update_outstanding=update_outstanding, merge_entries=False) + make_gl_entries(gl_entries, update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost) elif self.docstatus == 2: make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) diff --git a/erpnext/accounts/doctype/sales_invoice/test_records.json b/erpnext/accounts/doctype/sales_invoice/test_records.json index 11ebe6a573..ee6419db20 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_records.json +++ b/erpnext/accounts/doctype/sales_invoice/test_records.json @@ -17,7 +17,8 @@ "description": "138-CMS Shoe", "doctype": "Sales Invoice Item", "income_account": "Sales - _TC", - "expense_account": "_Test Account Cost for Goods Sold - _TC", + "expense_account": "_Test Account Cost for Goods Sold - _TC", + "item_code": "138-CMS Shoe", "item_name": "138-CMS Shoe", "parentfield": "items", "qty": 1.0, diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 22a4f33654..ceb7907989 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -10,7 +10,6 @@ from frappe.model.dynamic_links import get_dynamic_link_map from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile -from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError from frappe.model.naming import make_autoname @@ -659,7 +658,6 @@ class TestSalesInvoice(unittest.TestCase): def test_sales_invoice_gl_entry_without_perpetual_inventory(self): si = frappe.copy_doc(test_records[1]) - set_perpetual_inventory(0, si.company) si.insert() si.submit() @@ -815,7 +813,6 @@ class TestSalesInvoice(unittest.TestCase): frappe.db.sql("delete from `tabPOS Profile`") def test_pos_si_without_payment(self): - set_perpetual_inventory() make_pos_profile() pos = copy.deepcopy(test_records[1]) @@ -829,9 +826,8 @@ class TestSalesInvoice(unittest.TestCase): self.assertRaises(frappe.ValidationError, si.submit) def test_sales_invoice_gl_entry_with_perpetual_inventory_no_item_code(self): - set_perpetual_inventory() - - si = frappe.get_doc(test_records[1]) + si = create_sales_invoice(company="_Test Company with perpetual inventory", debit_to = "Debtors - TCP1", + income_account="Sales - TCP1", cost_center = "Main - TCP1", do_not_save=True) si.get("items")[0].item_code = None si.insert() si.submit() @@ -842,24 +838,16 @@ class TestSalesInvoice(unittest.TestCase): self.assertTrue(gl_entries) expected_values = dict((d[0], d) for d in [ - [si.debit_to, 630.0, 0.0], - [test_records[1]["items"][0]["income_account"], 0.0, 500.0], - [test_records[1]["taxes"][0]["account_head"], 0.0, 80.0], - [test_records[1]["taxes"][1]["account_head"], 0.0, 50.0], + ["Debtors - TCP1", 100.0, 0.0], + ["Sales - TCP1", 0.0, 100.0] ]) for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[gle.account][0], gle.account) self.assertEqual(expected_values[gle.account][1], gle.debit) self.assertEqual(expected_values[gle.account][2], gle.credit) - set_perpetual_inventory(0) - def test_sales_invoice_gl_entry_with_perpetual_inventory_non_stock_item(self): - set_perpetual_inventory() - si = frappe.get_doc(test_records[1]) - si.get("items")[0].item_code = "_Test Non Stock Item" - si.insert() - si.submit() + si = create_sales_invoice(item="_Test Non Stock Item") gl_entries = frappe.db.sql("""select account, debit, credit from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s @@ -867,17 +855,14 @@ class TestSalesInvoice(unittest.TestCase): self.assertTrue(gl_entries) expected_values = dict((d[0], d) for d in [ - [si.debit_to, 630.0, 0.0], - [test_records[1]["items"][0]["income_account"], 0.0, 500.0], - [test_records[1]["taxes"][0]["account_head"], 0.0, 80.0], - [test_records[1]["taxes"][1]["account_head"], 0.0, 50.0], + [si.debit_to, 100.0, 0.0], + [test_records[1]["items"][0]["income_account"], 0.0, 100.0] ]) for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[gle.account][0], gle.account) self.assertEqual(expected_values[gle.account][1], gle.debit) self.assertEqual(expected_values[gle.account][2], gle.credit) - set_perpetual_inventory(0) def _insert_purchase_receipt(self): from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import test_records \ @@ -1106,7 +1091,6 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(si.grand_total, 859.43) def test_multi_currency_gle(self): - set_perpetual_inventory(0) si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", currency="USD", conversion_rate=50) @@ -1776,64 +1760,69 @@ class TestSalesInvoice(unittest.TestCase): si.submit() target_doc = make_inter_company_transaction("Sales Invoice", si.name) + target_doc.items[0].update({ + "expense_account": "Cost of Goods Sold - _TC1", + "cost_center": "Main - _TC1", + "warehouse": "Stores - _TC1" + }) target_doc.submit() 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") + # 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) + # 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") + # 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") + # 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 = 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() + # 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() + # 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()] - ] + # 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)) + # 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()] - ] + # 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)) + # 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'): @@ -1991,14 +1980,19 @@ def create_sales_invoice(**args): si.append("items", { "item_code": args.item or args.item_code or "_Test Item", + "item_name": args.item_name or "_Test Item", + "description": args.description or "_Test Item", "gst_hsn_code": "999800", "warehouse": args.warehouse or "_Test Warehouse - _TC", "qty": args.qty or 1, + "uom": args.uom or "Nos", + "stock_uom": args.uom or "Nos", "rate": args.rate if args.get("rate") is not None else 100, "income_account": args.income_account or "Sales - _TC", "expense_account": args.expense_account or "Cost of Goods Sold - _TC", "cost_center": args.cost_center or "_Test Cost Center - _TC", - "serial_no": args.serial_no + "serial_no": args.serial_no, + "conversion_factor": 1 }) if not args.do_not_save: diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index fb3dd6a92a..3695075798 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-06-04 11:02:19", "doctype": "DocType", @@ -51,6 +52,7 @@ "column_break_24", "base_net_rate", "base_net_amount", + "incoming_rate", "drop_ship", "delivered_by_supplier", "accounting", @@ -792,20 +794,28 @@ "options": "Project" }, { - "depends_on": "eval:parent.update_stock == 1", - "fieldname": "sales_invoice_item", - "fieldtype": "Data", - "ignore_user_permissions": 1, - "label": "Sales Invoice Item", - "no_copy": 1, - "print_hide": 1, - "read_only": 1 - } + "depends_on": "eval:parent.update_stock == 1", + "fieldname": "sales_invoice_item", + "fieldtype": "Data", + "ignore_user_permissions": 1, + "label": "Sales Invoice Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "incoming_rate", + "fieldtype": "Currency", + "label": "Incoming Rate", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-08-20 11:24:41.749986", + "modified": "2020-09-23 19:59:04.879322", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 9a091bf57b..c7f0c8781c 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -15,13 +15,13 @@ class ClosedAccountingPeriod(frappe.ValidationError): pass class StockAccountInvalidTransaction(frappe.ValidationError): pass class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass -def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes'): +def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes', from_repost=False): if gl_map: if not cancel: validate_accounting_period(gl_map) gl_map = process_gl_map(gl_map, merge_entries) if gl_map and len(gl_map) > 1: - save_entries(gl_map, adv_adj, update_outstanding) + save_entries(gl_map, adv_adj, update_outstanding, from_repost) else: frappe.throw(_("Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction.")) else: @@ -119,8 +119,9 @@ def check_if_in_list(gle, gl_map, dimensions=None): if same_head: return e -def save_entries(gl_map, adv_adj, update_outstanding): - validate_cwip_accounts(gl_map) +def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False): + if not from_repost: + validate_cwip_accounts(gl_map) round_off_debit_credit(gl_map) @@ -128,24 +129,24 @@ def save_entries(gl_map, adv_adj, update_outstanding): check_freezing_date(gl_map[0]["posting_date"], adv_adj) for entry in gl_map: - make_entry(entry, adv_adj, update_outstanding) + make_entry(entry, adv_adj, update_outstanding, from_repost) - # check against budget - validate_expense_against_budget(entry) - - validate_account_for_perpetual_inventory(gl_map) + if not from_repost: + validate_account_for_perpetual_inventory(gl_map) -def make_entry(args, adv_adj, update_outstanding): +def make_entry(args, adv_adj, update_outstanding, from_repost=False): gle = frappe.new_doc("GL Entry") gle.update(args) gle.flags.ignore_permissions = 1 + gle.flags.from_repost = from_repost gle.insert() - gle.run_method("on_update_with_args", adv_adj, update_outstanding) + gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost) gle.submit() # check against budget - validate_expense_against_budget(args) + if not from_repost: + validate_expense_against_budget(args) def validate_account_for_perpetual_inventory(gl_map): if cint(erpnext.is_perpetual_inventory_enabled(gl_map[0].company)): @@ -161,7 +162,7 @@ def validate_account_for_perpetual_inventory(gl_map): # Always use current date to get stock and account balance as there can future entries for # other items account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(account, - getdate(), gl_map[0].company) + gl_map[0].posting_date, gl_map[0].company) if gl_map[0].voucher_type=="Journal Entry": # In case of Journal Entry, there are no corresponding SL entries, @@ -176,8 +177,8 @@ def validate_account_for_perpetual_inventory(gl_map): currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency")) diff = flt(stock_bal - account_bal, precision) - error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.").format( - stock_bal, account_bal, frappe.bold(account)) + error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses on {3}.").format( + stock_bal, account_bal, frappe.bold(account), gl_map[0].posting_date) error_resolution = _("Please create adjustment Journal Entry for amount {0} ").format(frappe.bold(diff)) stock_adjustment_account = frappe.db.get_value("Company",gl_map[0].company,"stock_adjustment_account") @@ -185,9 +186,10 @@ def validate_account_for_perpetual_inventory(gl_map): db_or_cr_stock_adjustment_account = ('debit_in_account_currency' if diff < 0 else 'credit_in_account_currency') journal_entry_args = { - 'accounts':[ - {'account': account, db_or_cr_warehouse_account : abs(diff)}, - {'account': stock_adjustment_account, db_or_cr_stock_adjustment_account : abs(diff) }] + 'accounts':[ + {'account': account, db_or_cr_warehouse_account : abs(diff)}, + {'account': stock_adjustment_account, db_or_cr_stock_adjustment_account : abs(diff)} + ] } frappe.msgprint(msg="""{0}

    {1}

    """.format(error_reason, error_resolution), diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 550aaef404..540ac84182 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -928,7 +928,7 @@ def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for if expected_gle: if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle): _delete_gl_entries(voucher_type, voucher_no) - voucher_obj.make_gl_entries(gl_entries=expected_gle, repost_future_gle=False, from_repost=True) + voucher_obj.make_gl_entries(gl_entries=expected_gle, from_repost=True) else: _delete_gl_entries(voucher_type, voucher_no) @@ -947,7 +947,10 @@ def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, f for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no from `tabStock Ledger Entry` sle - where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) {condition} + where + timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) + and is_cancelled = 0 + {condition} order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition), tuple([posting_date, posting_time] + values), as_dict=True): future_stock_vouchers.append([d.voucher_type, d.voucher_no]) @@ -964,3 +967,20 @@ def get_voucherwise_gl_entries(future_stock_vouchers, posting_date): gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d) return gl_entries + +def compare_existing_and_expected_gle(existing_gle, expected_gle): + matched = True + for entry in expected_gle: + account_existed = False + for e in existing_gle: + if entry.account == e.account: + account_existed = True + if entry.account == e.account and entry.against_account == e.against_account \ + and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center) \ + and (entry.debit != e.debit or entry.credit != e.credit): + matched = False + break + if not account_existed: + matched = False + break + return matched \ No newline at end of file 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 10db240a44..c691e9f9f8 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -732,7 +732,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-10-30 11:59:47.670951", + "modified": "2020-12-07 11:59:47.670951", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 286c4f4451..dc61870df3 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -16,6 +16,8 @@ from frappe.contacts.doctype.address.address import get_address_display from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget from erpnext.controllers.stock_controller import StockController +from erpnext.controllers.sales_and_purchase_return import get_rate_for_return +from erpnext.stock.utils import get_incoming_rate class BuyingController(StockController): def __setup__(self): @@ -63,7 +65,7 @@ class BuyingController(StockController): self.set_landed_cost_voucher_amount() if self.doctype in ("Purchase Receipt", "Purchase Invoice"): - self.update_valuation_rate("items") + self.update_valuation_rate() def set_missing_values(self, for_validate=False): super(BuyingController, self).set_missing_values(for_validate) @@ -177,7 +179,7 @@ class BuyingController(StockController): self.in_words = money_in_words(amount, self.currency) # update valuation rate - def update_valuation_rate(self, parentfield): + def update_valuation_rate(self, reset_outgoing_rate=True): """ item_tax_amount is the total tax amount applied on that item stored for valuation @@ -188,7 +190,7 @@ class BuyingController(StockController): stock_and_asset_items_qty, stock_and_asset_items_amount = 0, 0 last_item_idx = 1 - for d in self.get(parentfield): + for d in self.get("items"): if d.item_code and d.item_code in stock_and_asset_items: stock_and_asset_items_qty += flt(d.qty) stock_and_asset_items_amount += flt(d.base_net_amount) @@ -198,7 +200,7 @@ class BuyingController(StockController): if d.category in ["Valuation", "Valuation and Total"]]) valuation_amount_adjustment = total_valuation_amount - for i, item in enumerate(self.get(parentfield)): + for i, item in enumerate(self.get("items")): if item.item_code and item.qty and item.item_code in stock_and_asset_items: item_proportion = flt(item.base_net_amount) / stock_and_asset_items_amount if stock_and_asset_items_amount \ else flt(item.qty) / stock_and_asset_items_qty @@ -216,16 +218,34 @@ class BuyingController(StockController): item.conversion_factor = get_conversion_factor(item.item_code, item.uom).get("conversion_factor") or 1.0 qty_in_stock_uom = flt(item.qty * item.conversion_factor) - rm_supp_cost = flt(item.rm_supp_cost) if self.doctype in ["Purchase Receipt", "Purchase Invoice"] else 0.0 - - landed_cost_voucher_amount = flt(item.landed_cost_voucher_amount) \ - if self.doctype in ["Purchase Receipt", "Purchase Invoice"] else 0.0 - - item.valuation_rate = ((item.base_net_amount + item.item_tax_amount + rm_supp_cost - + landed_cost_voucher_amount) / qty_in_stock_uom) + item.rm_supp_cost = self.get_supplied_items_cost(item.name, reset_outgoing_rate) + item.valuation_rate = ((item.base_net_amount + item.item_tax_amount + item.rm_supp_cost + + flt(item.landed_cost_voucher_amount)) / qty_in_stock_uom) else: item.valuation_rate = 0.0 + def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True): + supplied_items_cost = 0.0 + for d in self.get("supplied_items"): + if d.reference_name == item_row_id: + if reset_outgoing_rate and frappe.db.get_value('Item', d.rm_item_code, 'is_stock_item'): + rate = get_incoming_rate({ + "item_code": d.rm_item_code, + "warehouse": self.supplier_warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "qty": -1 * d.consumed_qty, + "serial_no": d.serial_no + }) + + if rate > 0: + d.rate = rate + + d.amount = flt(d.consumed_qty) * flt(d.rate) + supplied_items_cost += flt(d.amount) + + return supplied_items_cost + def validate_for_subcontracting(self): if not self.is_subcontracted and self.sub_contracted_items: frappe.throw(_("Please enter 'Is Subcontracted' as Yes or No")) @@ -352,35 +372,17 @@ class BuyingController(StockController): else: self.append_raw_material_to_be_backflushed(item, raw_material, qty) - def append_raw_material_to_be_backflushed(self, fg_item_doc, raw_material_data, qty): + def append_raw_material_to_be_backflushed(self, fg_item_row, raw_material_data, qty): rm = self.append('supplied_items', {}) rm.update(raw_material_data) if not rm.main_item_code: - rm.main_item_code = fg_item_doc.item_code + rm.main_item_code = fg_item_row.item_code - rm.reference_name = fg_item_doc.name + rm.reference_name = fg_item_row.name rm.required_qty = qty rm.consumed_qty = qty - if not raw_material_data.get('non_stock_item'): - from erpnext.stock.utils import get_incoming_rate - rm.rate = get_incoming_rate({ - "item_code": raw_material_data.rm_item_code, - "warehouse": self.supplier_warehouse, - "posting_date": self.posting_date, - "posting_time": self.posting_time, - "qty": -1 * qty, - "serial_no": rm.serial_no - }) - - if not rm.rate: - rm.rate = get_valuation_rate(raw_material_data.rm_item_code, self.supplier_warehouse, - self.doctype, self.name, currency=self.company_currency, company=self.company) - - rm.amount = qty * flt(rm.rate) - fg_item_doc.rm_supp_cost += rm.amount - def update_raw_materials_supplied_based_on_bom(self, item, raw_material_table): exploded_item = 1 if hasattr(item, 'include_exploded_items'): @@ -389,7 +391,7 @@ class BuyingController(StockController): bom_items = get_items_from_bom(item.item_code, item.bom, exploded_item) used_alternative_items = [] - if self.doctype == 'Purchase Receipt' and item.purchase_order: + if self.doctype in ["Purchase Receipt", "Purchase Invoice"] and item.purchase_order: used_alternative_items = get_used_alternative_items(purchase_order = item.purchase_order) raw_materials_cost = 0 @@ -406,7 +408,7 @@ class BuyingController(StockController): reserve_warehouse = None conversion_factor = item.conversion_factor - if (self.doctype == 'Purchase Receipt' and item.purchase_order and + if (self.doctype in ["Purchase Receipt", "Purchase Invoice"] and item.purchase_order and bom_item.item_code in used_alternative_items): alternative_item_data = used_alternative_items.get(bom_item.item_code) bom_item.item_code = alternative_item_data.item_code @@ -434,9 +436,7 @@ class BuyingController(StockController): rm.rm_item_code = bom_item.item_code rm.stock_uom = bom_item.stock_uom rm.required_qty = required_qty - if self.doctype == "Purchase Order" and not rm.reserve_warehouse: - rm.reserve_warehouse = reserve_warehouse - + rm.rate = bom_item.rate rm.conversion_factor = conversion_factor if self.doctype in ["Purchase Receipt", "Purchase Invoice"]: @@ -444,29 +444,8 @@ class BuyingController(StockController): rm.description = bom_item.description if item.batch_no and frappe.db.get_value("Item", rm.rm_item_code, "has_batch_no") and not rm.batch_no: rm.batch_no = item.batch_no - - # get raw materials rate - if self.doctype == "Purchase Receipt": - from erpnext.stock.utils import get_incoming_rate - rm.rate = get_incoming_rate({ - "item_code": bom_item.item_code, - "warehouse": self.supplier_warehouse, - "posting_date": self.posting_date, - "posting_time": self.posting_time, - "qty": -1 * required_qty, - "serial_no": rm.serial_no - }) - if not rm.rate: - rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse, - self.doctype, self.name, currency=self.company_currency, company = self.company) - else: - rm.rate = bom_item.rate - - rm.amount = required_qty * flt(rm.rate) - raw_materials_cost += flt(rm.amount) - - if self.doctype in ("Purchase Receipt", "Purchase Invoice"): - item.rm_supp_cost = raw_materials_cost + elif not rm.reserve_warehouse: + rm.reserve_warehouse = reserve_warehouse def cleanup_raw_materials_supplied(self, parent_items, raw_material_table): """Remove all those child items which are no longer present in main item table""" @@ -579,7 +558,8 @@ class BuyingController(StockController): or (cint(self.is_return) and self.docstatus==2)): from_warehouse_sle = self.get_sl_entries(d, { "actual_qty": -1 * pr_qty, - "warehouse": d.from_warehouse + "warehouse": d.from_warehouse, + "dependant_sle_voucher_detail_no": d.name }) sl_entries.append(from_warehouse_sle) @@ -589,28 +569,20 @@ class BuyingController(StockController): "serial_no": cstr(d.serial_no).strip() }) if self.is_return: - filters = { - "voucher_type": self.doctype, - "voucher_no": self.return_against, - "item_code": d.item_code - } - - if (self.doctype == "Purchase Invoice" and self.update_stock - and d.get("purchase_invoice_item")): - filters["voucher_detail_no"] = d.purchase_invoice_item - elif self.doctype == "Purchase Receipt" and d.get("purchase_receipt_item"): - filters["voucher_detail_no"] = d.purchase_receipt_item - - original_incoming_rate = frappe.db.get_value("Stock Ledger Entry", filters, "incoming_rate") + outgoing_rate = get_rate_for_return(self.doctype, self.name, d.item_code, self.return_against, item_row=d) sle.update({ - "outgoing_rate": original_incoming_rate + "outgoing_rate": outgoing_rate, + "recalculate_rate": 1 }) + if d.from_warehouse: + sle.dependant_sle_voucher_detail_no = d.name else: val_rate_db_precision = 6 if cint(self.precision("valuation_rate", d)) <= 6 else 9 incoming_rate = flt(d.valuation_rate, val_rate_db_precision) sle.update({ - "incoming_rate": incoming_rate + "incoming_rate": incoming_rate, + "recalculate_rate": 1 if (self.is_subcontracted and d.bom) or d.from_warehouse else 0 }) sl_entries.append(sle) @@ -618,7 +590,8 @@ class BuyingController(StockController): or (cint(self.is_return) and self.docstatus==1)): from_warehouse_sle = self.get_sl_entries(d, { "actual_qty": -1 * pr_qty, - "warehouse": d.from_warehouse + "warehouse": d.from_warehouse, + "recalculate_rate": 1 }) sl_entries.append(from_warehouse_sle) @@ -666,6 +639,7 @@ class BuyingController(StockController): "item_code": d.rm_item_code, "warehouse": self.supplier_warehouse, "actual_qty": -1*flt(d.consumed_qty), + "dependant_sle_voucher_detail_no": d.reference_name })) def on_submit(self): @@ -857,6 +831,7 @@ class BuyingController(StockController): else: validate_item_type(self, "is_purchase_item", "purchase") + def get_items_from_bom(item_code, bom, exploded_item=1): doctype = "BOM Item" if not exploded_item else "BOM Explosion Item" diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 5299b25601..8f65c31f3d 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -365,3 +365,45 @@ def make_return_doc(doctype, source_name, target_doc=None): }, target_doc, set_missing_values) return doclist + +def get_rate_for_return(voucher_type, voucher_no, item_code, return_against=None, item_row=None, voucher_detail_no=None): + if not return_against: + return_against = frappe.get_cached_value(voucher_type, voucher_no, "return_against") + + return_against_item_field = get_return_against_item_fields(voucher_type) + + filters = get_filters(voucher_type, voucher_no, voucher_detail_no, + return_against, item_code, return_against_item_field, item_row) + + if voucher_type in ("Purchase Receipt", "Purchase Invoice"): + select_field = "incoming_rate" + else: + select_field = "abs(stock_value_difference / actual_qty)" + + return flt(frappe.db.get_value("Stock Ledger Entry", filters, select_field)) + +def get_return_against_item_fields(voucher_type): + return_against_item_fields = { + "Purchase Receipt": "purchase_receipt_item", + "Purchase Invoice": "purchase_invoice_item", + "Delivery Note": "dn_detail", + "Sales Invoice": "sales_invoice_item" + } + return return_against_item_fields[voucher_type] + +def get_filters(voucher_type, voucher_no, voucher_detail_no, return_against, item_code, return_against_item_field, item_row): + filters = { + "voucher_type": voucher_type, + "voucher_no": return_against, + "item_code": item_code + } + + if item_row: + reference_voucher_detail_no = item_row.get(return_against_item_field) + else: + reference_voucher_detail_no = frappe.db.get_value(voucher_type + " Item", voucher_detail_no, return_against_item_field) + + if reference_voucher_detail_no: + filters["voucher_detail_no"] = reference_voucher_detail_no + + return filters \ No newline at end of file diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 4dbd7bfa18..85cfb951fc 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -13,6 +13,7 @@ from frappe.contacts.doctype.address.address import get_address_display from erpnext.controllers.accounts_controller import get_taxes_and_charges from erpnext.controllers.stock_controller import StockController +from erpnext.controllers.sales_and_purchase_return import get_rate_for_return class SellingController(StockController): def __setup__(self): @@ -48,6 +49,7 @@ class SellingController(StockController): self.set_customer_address() self.validate_for_duplicate_items() self.validate_target_warehouse() + self.set_incoming_rate() def set_missing_values(self, for_validate=False): @@ -230,7 +232,8 @@ class SellingController(StockController): 'voucher_type': self.doctype, 'allow_zero_valuation': d.allow_zero_valuation_rate, 'sales_invoice_item': d.get("sales_invoice_item"), - 'delivery_note_item': d.get("dn_detail") + 'dn_detail': d.get("dn_detail"), + 'incoming_rate': p.incoming_rate })) else: il.append(frappe._dict({ @@ -248,7 +251,8 @@ class SellingController(StockController): 'voucher_type': self.doctype, 'allow_zero_valuation': d.allow_zero_valuation_rate, 'sales_invoice_item': d.get("sales_invoice_item"), - 'delivery_note_item': d.get("dn_detail") + 'dn_detail': d.get("dn_detail"), + 'incoming_rate': d.incoming_rate })) return il @@ -307,69 +311,89 @@ class SellingController(StockController): sales_order.update_reserved_qty(so_item_rows) + def set_incoming_rate(self): + if self.doctype not in ("Delivery Note", "Sales Invoice"): + return + + items = self.get("items") + (self.get("packed_items") or []) + for d in items: + if not cint(self.get("is_return")): + # Get incoming rate based on original item cost based on valuation method + d.incoming_rate = get_incoming_rate({ + "item_code": d.item_code, + "warehouse": d.warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "qty": -1*flt(d.qty), + "serial_no": d.serial_no, + "company": self.company, + "voucher_type": self.doctype, + "voucher_no": self.name, + "allow_zero_valuation": d.get("allow_zero_valuation") + }, raise_error_if_no_rate=False) + elif self.get("return_against"): + # Get incoming rate of return entry from reference document + # based on original item cost as per valuation method + d.incoming_rate = get_rate_for_return(self.doctype, self.name, d.item_code, self.return_against, item_row=d) + def update_stock_ledger(self): self.update_reserved_qty() sl_entries = [] + # Loop over items and packed items table for d in self.get_item_list(): if frappe.get_cached_value("Item", d.item_code, "is_stock_item") == 1 and flt(d.qty): if flt(d.conversion_factor)==0.0: d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0 - return_rate = 0 - if cint(self.is_return) and self.return_against and self.docstatus==1: - against_document_no = (d.get("sales_invoice_item") - if self.doctype == "Sales Invoice" else d.get("delivery_note_item")) - return_rate = self.get_incoming_rate_for_return(d.item_code, - self.return_against, against_document_no) - - # On cancellation or if return entry submission, make stock ledger entry for + # On cancellation or return entry submission, make stock ledger entry for # target warehouse first, to update serial no values properly if d.warehouse and ((not cint(self.is_return) and self.docstatus==1) or (cint(self.is_return) and self.docstatus==2)): - sl_entries.append(self.get_sl_entries(d, { - "actual_qty": -1*flt(d.qty), - "incoming_rate": return_rate - })) + sl_entries.append(self.get_sle_for_source_warehouse(d)) if d.target_warehouse: - target_warehouse_sle = self.get_sl_entries(d, { - "actual_qty": flt(d.qty), - "warehouse": d.target_warehouse - }) - - if self.docstatus == 1: - if not cint(self.is_return): - args = frappe._dict({ - "item_code": d.item_code, - "warehouse": d.warehouse, - "posting_date": self.posting_date, - "posting_time": self.posting_time, - "qty": -1*flt(d.qty), - "serial_no": d.serial_no, - "company": d.company, - "voucher_type": d.voucher_type, - "voucher_no": d.name, - "allow_zero_valuation": d.allow_zero_valuation - }) - target_warehouse_sle.update({ - "incoming_rate": get_incoming_rate(args) - }) - else: - target_warehouse_sle.update({ - "outgoing_rate": return_rate - }) - sl_entries.append(target_warehouse_sle) + sl_entries.append(self.get_sle_for_target_warehouse(d)) if d.warehouse and ((not cint(self.is_return) and self.docstatus==2) or (cint(self.is_return) and self.docstatus==1)): - sl_entries.append(self.get_sl_entries(d, { - "actual_qty": -1*flt(d.qty), - "incoming_rate": return_rate - })) + sl_entries.append(self.get_sle_for_source_warehouse(d)) + self.make_sl_entries(sl_entries) + def get_sle_for_source_warehouse(self, item_row): + sle = self.get_sl_entries(item_row, { + "actual_qty": -1*flt(item_row.qty), + "incoming_rate": item_row.incoming_rate, + "recalculate_rate": cint(self.is_return) + }) + if item_row.target_warehouse and not cint(self.is_return): + sle.dependant_sle_voucher_detail_no = item_row.name + + return sle + + def get_sle_for_target_warehouse(self, item_row): + sle = self.get_sl_entries(item_row, { + "actual_qty": flt(item_row.qty), + "warehouse": item_row.target_warehouse + }) + + if self.docstatus == 1: + if not cint(self.is_return): + sle.update({ + "incoming_rate": item_row.incoming_rate, + "recalculate_rate": 1 + }) + else: + sle.update({ + "outgoing_rate": item_row.incoming_rate + }) + if item_row.warehouse: + sle.dependant_sle_voucher_detail_no = item_row.name + + return sle + def set_po_nos(self, for_validate=False): if self.doctype == 'Sales Invoice' and hasattr(self, "items"): if for_validate and self.po_no: @@ -463,4 +487,4 @@ def set_default_income_account_for_item(obj): for d in obj.get("items"): if d.item_code: if getattr(d, "income_account", None): - set_item_default(d.item_code, obj.company, 'income_account', d.income_account) + set_item_default(d.item_code, obj.company, 'income_account', d.income_account) \ No newline at end of file diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 683d7f77b5..51c063c2c0 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -24,7 +24,7 @@ class StockController(AccountsController): self.validate_serialized_batch() self.validate_customer_provided_item() - def make_gl_entries(self, gl_entries=None): + def make_gl_entries(self, gl_entries=None, from_repost=False): if self.docstatus == 2: make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) @@ -34,12 +34,12 @@ class StockController(AccountsController): if self.docstatus==1: if not gl_entries: gl_entries = self.get_gl_entries(warehouse_account) - make_gl_entries(gl_entries) + make_gl_entries(gl_entries, from_repost=from_repost) elif self.doctype in ['Purchase Receipt', 'Purchase Invoice'] and self.docstatus == 1: gl_entries = [] gl_entries = self.get_asset_gl_entry(gl_entries) - make_gl_entries(gl_entries) + make_gl_entries(gl_entries, from_repost=from_repost) def validate_serialized_batch(self): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -70,7 +70,6 @@ class StockController(AccountsController): gl_list = [] warehouse_with_no_account = [] - precision = frappe.get_precision("GL Entry", "debit_in_account_currency") for item_row in voucher_details: sle_list = sle_map.get(item_row.name) @@ -125,7 +124,7 @@ class StockController(AccountsController): if warehouse_with_no_account: for wh in warehouse_with_no_account: if frappe.db.get_value("Warehouse", wh, "company"): - frappe.throw(_("Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.").format(wh, self.company)) + frappe.throw(_("Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.").format(wh, self.company)) return process_gl_map(gl_list) @@ -309,23 +308,6 @@ class StockController(AccountsController): return serialized_items - def get_incoming_rate_for_return(self, item_code, against_document, against_document_no=None): - incoming_rate = 0.0 - cond = '' - if against_document and item_code: - if against_document_no: - cond = " and voucher_detail_no = %s" %(frappe.db.escape(against_document_no)) - - incoming_rate = frappe.db.sql("""select abs(stock_value_difference / actual_qty) - from `tabStock Ledger Entry` - where voucher_type = %s and voucher_no = %s - and item_code = %s {0} limit 1""".format(cond), - (self.doctype, against_document, item_code)) - - incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0 - - return incoming_rate - def validate_warehouse(self): from erpnext.stock.utils import validate_warehouse_company @@ -409,19 +391,64 @@ class StockController(AccountsController): if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): d.allow_zero_valuation_rate = 1 -def compare_existing_and_expected_gle(existing_gle, expected_gle): - matched = True - for entry in expected_gle: - account_existed = False - for e in existing_gle: - if entry.account == e.account: - account_existed = True - if entry.account == e.account and entry.against_account == e.against_account \ - and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center) \ - and (entry.debit != e.debit or entry.credit != e.credit): - matched = False - break - if not account_existed: - matched = False + def repost_future_sle_and_gle(self): + args = frappe._dict({ + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "voucher_type": self.doctype, + "voucher_no": self.name, + "company": self.company + }) + + if check_if_future_sle_exists(args): + create_repost_item_valuation_entry(args) + +def check_if_future_sle_exists(args): + sl_entries = frappe.db.get_all("Stock Ledger Entry", + filters={"voucher_type": args.voucher_type, "voucher_no": args.voucher_no}, + fields=["item_code", "warehouse"], + order_by="creation asc") + + distinct_item_warehouses = list(set([(d.item_code, d.warehouse) for d in sl_entries])) + + sle_exists = False + for item_code, warehouse in distinct_item_warehouses: + args.update({ + "item_code": item_code, + "warehouse": warehouse + }) + if get_sle(args): + sle_exists = True break - return matched + return sle_exists + +def get_sle(args): + return frappe.db.sql(""" + select name + from `tabStock Ledger Entry` + where + item_code=%(item_code)s + and warehouse=%(warehouse)s + and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s) + and voucher_no != %(voucher_no)s + and is_cancelled = 0 + limit 1 + """, args) + +def create_repost_item_valuation_entry(args): + args = frappe._dict(args) + repost_entry = frappe.new_doc("Repost Item Valuation") + repost_entry.based_on = args.based_on + if not args.based_on: + repost_entry.based_on = 'Transaction' if args.voucher_no else "Item and Warehouse" + repost_entry.voucher_type = args.voucher_type + repost_entry.voucher_no = args.voucher_no + repost_entry.item_code = args.item_code + repost_entry.warehouse = args.warehouse + repost_entry.posting_date = args.posting_date + repost_entry.posting_time = args.posting_time + repost_entry.company = args.company + repost_entry.allow_zero_rate = args.allow_zero_rate + repost_entry.flags.ignore_links = True + repost_entry.save() + repost_entry.submit() \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 2bf3fbf75e..ce9699e1b3 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -6,7 +6,6 @@ from __future__ import unicode_literals import unittest import frappe from frappe.utils import flt, time_diff_in_hours, now, add_months, cint, today -from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory from erpnext.manufacturing.doctype.work_order.work_order import (make_stock_entry, ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError, CapacityError) from erpnext.stock.doctype.stock_entry import test_stock_entry @@ -18,7 +17,6 @@ from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse class TestWorkOrder(unittest.TestCase): def setUp(self): - set_perpetual_inventory(0) self.warehouse = '_Test Warehouse 2 - _TC' self.item = '_Test Item' diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index eff17f8bc7..159655b74b 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -785,7 +785,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-05-29 20:54:32.309460", + "modified": "2020-012-07 20:54:32.309460", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/setup/doctype/company/test_records.json b/erpnext/setup/doctype/company/test_records.json index 21302417d2..9e55702ddc 100644 --- a/erpnext/setup/doctype/company/test_records.json +++ b/erpnext/setup/doctype/company/test_records.json @@ -7,7 +7,8 @@ "doctype": "Company", "domain": "Manufacturing", "chart_of_accounts": "Standard", - "default_holiday_list": "_Test Holiday List" + "default_holiday_list": "_Test Holiday List", + "enable_perpetual_inventory": 0 }, { "abbr": "_TC1", @@ -17,7 +18,8 @@ "doctype": "Company", "domain": "Retail", "chart_of_accounts": "Standard", - "default_holiday_list": "_Test Holiday List" + "default_holiday_list": "_Test Holiday List", + "enable_perpetual_inventory": 0 }, { "abbr": "_TC2", @@ -27,7 +29,8 @@ "doctype": "Company", "domain": "Retail", "chart_of_accounts": "Standard", - "default_holiday_list": "_Test Holiday List" + "default_holiday_list": "_Test Holiday List", + "enable_perpetual_inventory": 0 }, { "abbr": "_TC3", @@ -38,7 +41,8 @@ "doctype": "Company", "domain": "Manufacturing", "chart_of_accounts": "Standard", - "default_holiday_list": "_Test Holiday List" + "default_holiday_list": "_Test Holiday List", + "enable_perpetual_inventory": 0 }, { "abbr": "_TC4", @@ -50,7 +54,8 @@ "doctype": "Company", "domain": "Manufacturing", "chart_of_accounts": "Standard", - "default_holiday_list": "_Test Holiday List" + "default_holiday_list": "_Test Holiday List", + "enable_perpetual_inventory": 0 }, { "abbr": "_TC5", @@ -61,7 +66,8 @@ "doctype": "Company", "domain": "Manufacturing", "chart_of_accounts": "Standard", - "default_holiday_list": "_Test Holiday List" + "default_holiday_list": "_Test Holiday List", + "enable_perpetual_inventory": 0 }, { "abbr": "TCP1", diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py index c2a3d3c151..e41f1a8aaa 100644 --- a/erpnext/stock/doctype/batch/test_batch.py +++ b/erpnext/stock/doctype/batch/test_batch.py @@ -8,13 +8,8 @@ import unittest from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no from frappe.utils import cint, flt -from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory class TestBatch(unittest.TestCase): - - def setUp(self): - set_perpetual_inventory(0) - def test_item_has_batch_enabled(self): self.assertRaises(ValidationError, frappe.get_doc({ "doctype": "Batch", diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 7acdec728b..ab19b77ad8 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -16,22 +16,30 @@ class Bin(Document): def update_stock(self, args, allow_negative_stock=False, via_landed_cost_voucher=False): '''Called from erpnext.stock.utils.update_bin''' self.update_qty(args) - if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation": - from erpnext.stock.stock_ledger import update_entries_after + from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle if not args.get("posting_date"): args["posting_date"] = nowdate() + if args.get("is_cancelled") and via_landed_cost_voucher: + return + + # Reposts only current voucher SL Entries + # Updates valuation rate, stock value, stock queue for current transaction update_entries_after({ "item_code": self.item_code, "warehouse": self.warehouse, "posting_date": args.get("posting_date"), "posting_time": args.get("posting_time"), + "voucher_type": args.get("voucher_type"), "voucher_no": args.get("voucher_no"), - "sle_id": args.sle_id + "sle_id": args.name }, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher) + # Update qty_after_transaction in future SLEs of this item and warehouse + update_qty_in_future_sle(args) + def update_qty(self, args): # update the stock values (for current quantities) if args.get("voucher_type")=="Stock Reconciliation": diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 3f3407e350..1a6a555092 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -217,6 +217,7 @@ class DeliveryNote(SellingController): # because updating reserved qty in bin depends upon updated delivered qty in SO self.update_stock_ledger() self.make_gl_entries() + self.repost_future_sle_and_gle() def on_cancel(self): super(DeliveryNote, self).on_cancel() @@ -234,7 +235,8 @@ class DeliveryNote(SellingController): self.cancel_packing_slips() self.make_gl_entries_on_cancel() - self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') + self.repost_future_sle_and_gle() + self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation') def check_credit_limit(self): from erpnext.selling.doctype.customer.customer import check_credit_limit diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 6b4663a688..559f8be0de 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -10,8 +10,7 @@ import frappe.defaults from frappe.utils import cint, nowdate, nowtime, cstr, add_days, flt, today from erpnext.stock.stock_ledger import get_previous_sle from erpnext.accounts.utils import get_balance_on -from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \ - import get_gl_entries, set_perpetual_inventory +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice, make_delivery_trip from erpnext.stock.doctype.stock_entry.test_stock_entry \ import make_stock_entry, make_serialized_item, get_qty_after_transaction @@ -24,9 +23,6 @@ from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse from erpnext.stock.doctype.item.test_item import create_item class TestDeliveryNote(unittest.TestCase): - def setUp(self): - set_perpetual_inventory(0) - def test_over_billing_against_dn(self): frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) @@ -43,7 +39,6 @@ class TestDeliveryNote(unittest.TestCase): def test_delivery_note_no_gl_entry(self): company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') - set_perpetual_inventory(0, company) make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100) stock_queue = json.loads(get_previous_sle({ 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 7b471874af..4bbf3de594 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -56,6 +56,7 @@ "base_net_rate", "base_net_amount", "billed_amt", + "incoming_rate", "item_weight_details", "weight_per_unit", "total_weight", @@ -732,16 +733,22 @@ "depends_on": "returned_qty", "fieldname": "returned_qty", "fieldtype": "Float", - "label": "Returned Qty in Stock UOM", + "label": "Returned Qty in Stock UOM" + }, + { + "fieldname": "incoming_rate", + "fieldtype": "Currency", + "label": "Incoming Rate", "no_copy": 1, "print_hide": 1, "read_only": 1 } ], "idx": 1, + "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-07-31 20:12:43.054342", + "modified": "2020-12-07 19:59:27.119856", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/item/test_records.json b/erpnext/stock/doctype/item/test_records.json index 9ca887c77e..8f437b13f0 100644 --- a/erpnext/stock/doctype/item/test_records.json +++ b/erpnext/stock/doctype/item/test_records.json @@ -458,5 +458,15 @@ "item_tax_template": "_Test Item Tax Template 1" } ] + }, + { + "description": "_Test", + "doctype": "Item", + "is_stock_item": 1, + "item_code": "138-CMS Shoe", + "item_group": "_Test Item Group", + "item_name": "138-CMS Shoe", + "stock_uom": "_Test UOM", + "gst_hsn_code": "999800" } ] diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.py b/erpnext/stock/doctype/item_alternative/test_item_alternative.py index f045e4f911..d5700fe514 100644 --- a/erpnext/stock/doctype/item_alternative/test_item_alternative.py +++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.py @@ -12,11 +12,9 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt, make_rm_stock_entry import unittest -from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory class TestItemAlternative(unittest.TestCase): def setUp(self): - set_perpetual_inventory(0) make_items() def test_alternative_item_for_subcontract_rm(self): diff --git a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json index 0cc243d4cb..64331c7d57 100644 --- a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json +++ b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2014-07-11 11:51:00.453717", "doctype": "DocType", "editable_grid": 1, @@ -31,16 +32,19 @@ "reqd": 1 }, { + "depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))", "fieldname": "expense_account", "fieldtype": "Link", "in_list_view": 1, "label": "Expense Account", + "mandatory_depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))", "options": "Account", - "reqd": 1 + "print_hide": 1 } ], "istable": 1, - "modified": "2019-09-30 18:28:32.070655", + "links": [], + "modified": "2020-12-04 00:22:14.373312", "modified_by": "Administrator", "module": "Stock", "name": "Landed Cost Taxes and Charges", diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index bc3d3266ad..9ec6b8946c 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -77,9 +77,9 @@ class LandedCostVoucher(Document): company_currency = erpnext.get_company_currency(self.company) for account in self.taxes: if get_account_currency(account.expense_account) != company_currency: - frappe.throw(msg=_(""" Row {0}: Expense account currency should be same as company's default currency. - Please select expense account with account currency as {1}""") - .format(account.idx, frappe.bold(company_currency)), title=_("Invalid Account Currency")) + frappe.throw(_("Row {}: Expense account currency should be same as company's default currency.").format(account.idx) + + _("Please select expense account with account currency as {}.").format(frappe.bold(company_currency)), + title=_("Invalid Account Currency")) def set_total_taxes_and_charges(self): self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("taxes")]) @@ -121,7 +121,7 @@ class LandedCostVoucher(Document): doc.set_landed_cost_voucher_amount() # set valuation amount in pr item - doc.update_valuation_rate("items") + doc.update_valuation_rate(reset_outgoing_rate=False) # db_update will update and save landed_cost_voucher_amount and voucher_amount in PR for item in doc.get("items"): @@ -143,6 +143,7 @@ class LandedCostVoucher(Document): doc.docstatus = 1 doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True) doc.make_gl_entries() + doc.repost_future_sle_and_gle() def validate_asset_qty_and_status(self, receipt_document_type, receipt_document): for item in self.get('items'): @@ -152,14 +153,13 @@ class LandedCostVoucher(Document): docs = frappe.db.get_all('Asset', filters={ receipt_document_type: item.receipt_document, 'item_code': item.item_code }, fields=['name', 'docstatus']) if not docs or len(docs) != item.qty: - frappe.throw(_('There are not enough asset created or linked to {0}. \ - Please create or link {1} Assets with respective document.').format(item.receipt_document, item.qty)) + frappe.throw(_('There are not enough asset created or linked to {0}.').format(item.receipt_document) + + _('Please create or link {0} Assets with respective document.').format(item.qty)) if docs: for d in docs: if d.docstatus == 1: - frappe.throw(_('{2} {0} has submitted Assets.\ - Remove Item {1} from table to continue.').format( - item.receipt_document, item.item_code, item.receipt_document_type)) + frappe.throw(_('{0} {1} has submitted Assets. Remove Item {2} from table to continue.') + .format(item.receipt_document_type, frappe.bold(item.receipt_document), frappe.bold(item.item_code))) def update_rate_in_serial_no_for_non_asset_items(self, receipt_document): for item in receipt_document.get("items"): diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py index 3f2c5daf66..b97213e4fb 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py @@ -7,7 +7,7 @@ import unittest import frappe from frappe.utils import flt from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \ - import set_perpetual_inventory, get_gl_entries, test_records as pr_test_records, make_purchase_receipt + import get_gl_entries, test_records as pr_test_records, make_purchase_receipt from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.account.test_account import get_inventory_account @@ -27,7 +27,7 @@ class TestLandedCostVoucher(unittest.TestCase): }, fieldname=["qty_after_transaction", "stock_value"], as_dict=1) - submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) + create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) pr_lc_value = frappe.db.get_value("Purchase Receipt Item", {"parent": pr.name}, "landed_cost_voucher_amount") self.assertEqual(pr_lc_value, 25.0) @@ -89,7 +89,7 @@ class TestLandedCostVoucher(unittest.TestCase): }, fieldname=["qty_after_transaction", "stock_value"], as_dict=1) - submit_landed_cost_voucher("Purchase Invoice", pi.name, pi.company) + create_landed_cost_voucher("Purchase Invoice", pi.name, pi.company) pi_lc_value = frappe.db.get_value("Purchase Invoice Item", {"parent": pi.name}, "landed_cost_voucher_amount") @@ -137,7 +137,7 @@ class TestLandedCostVoucher(unittest.TestCase): serial_no_rate = frappe.db.get_value("Serial No", "SN001", "purchase_rate") - submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) + create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) serial_no = frappe.db.get_value("Serial No", "SN001", ["warehouse", "purchase_rate"], as_dict=1) @@ -160,7 +160,7 @@ class TestLandedCostVoucher(unittest.TestCase): }) pr.submit() - lcv = submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22) + lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22) self.assertEqual(lcv.items[0].applicable_charges, 41.07) self.assertEqual(lcv.items[2].applicable_charges, 41.08) @@ -236,7 +236,7 @@ def make_landed_cost_voucher(** args): return lcv -def submit_landed_cost_voucher(receipt_document_type, receipt_document, company, charges=50): +def create_landed_cost_voucher(receipt_document_type, receipt_document, company, charges=50): ref_doc = frappe.get_doc(receipt_document_type, receipt_document) lcv = frappe.new_doc("Landed Cost Voucher") diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 19924b1636..0a29fa05e1 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -12,9 +12,6 @@ from erpnext.stock.doctype.material_request.material_request \ from erpnext.stock.doctype.item.test_item import create_item class TestMaterialRequest(unittest.TestCase): - def setUp(self): - erpnext.set_perpetual_inventory(0) - def test_make_purchase_order(self): mr = frappe.copy_doc(test_records[0]).insert() diff --git a/erpnext/stock/doctype/packed_item/packed_item.json b/erpnext/stock/doctype/packed_item/packed_item.json index 2ac5c426c0..f1d7f8c8c9 100644 --- a/erpnext/stock/doctype/packed_item/packed_item.json +++ b/erpnext/stock/doctype/packed_item/packed_item.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2013-02-22 01:28:00", "doctype": "DocType", "editable_grid": 1, @@ -14,6 +15,7 @@ "target_warehouse", "column_break_9", "qty", + "uom", "section_break_9", "serial_no", "column_break_11", @@ -23,7 +25,7 @@ "actual_qty", "projected_qty", "column_break_16", - "uom", + "incoming_rate", "page_break", "prevdoc_doctype", "parent_detail_docname" @@ -199,11 +201,21 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "incoming_rate", + "fieldtype": "Currency", + "label": "Incoming Rate", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, + "index_web_pages_for_search": 1, "istable": 1, - "modified": "2019-11-26 20:09:59.400960", + "links": [], + "modified": "2020-09-24 09:25:13.050151", "modified_by": "Administrator", "module": "Stock", "name": "Packed Item", @@ -212,4 +224,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 97e0fa738c..226064bae7 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -181,6 +181,7 @@ class PurchaseReceipt(BuyingController): update_serial_nos_after_submit(self, "items") self.make_gl_entries() + self.repost_future_sle_and_gle() def check_next_docstatus(self): submit_rv = frappe.db.sql("""select t1.name @@ -209,7 +210,8 @@ class PurchaseReceipt(BuyingController): # because updating ordered qty in bin depends upon updated ordered qty in PO self.update_stock_ledger() self.make_gl_entries_on_cancel() - self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') + self.repost_future_sle_and_gle() + self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation') self.delete_auto_created_batches() def get_current_stock(self): diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 9b8eeed1a1..83012d355f 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -9,14 +9,15 @@ import frappe.defaults from frappe.utils import cint, flt, cstr, today, random_string, add_days from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice from erpnext.stock.doctype.item.test_item import create_item -from erpnext import set_perpetual_inventory from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.stock.doctype.item.test_item import make_item from six import iteritems +from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + class TestPurchaseReceipt(unittest.TestCase): def setUp(self): - set_perpetual_inventory(0) frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1) def test_reverse_purchase_receipt_sle(self): @@ -112,6 +113,8 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertFalse(get_gl_entries("Purchase Receipt", pr.name)) + pr.cancel() + def test_batched_serial_no_purchase(self): item = frappe.db.exists("Item", {'item_name': 'Batched Serialized Item'}) if not item: @@ -183,22 +186,30 @@ class TestPurchaseReceipt(unittest.TestCase): rm_supp_cost = sum([d.amount for d in pr.get("supplied_items")]) self.assertEqual(pr.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2)) + + pr.cancel() def test_subcontracting_gle_fg_item_rate_zero(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry - set_perpetual_inventory() frappe.db.set_value("Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM") - make_stock_entry(item_code="_Test Item", target="Work In Progress - TCP1", qty=100, basic_rate=100, company="_Test Company with perpetual inventory") - make_stock_entry(item_code="_Test Item Home Desktop 100", target="Work In Progress - TCP1", + + se1 = make_stock_entry(item_code="_Test Item", target="Work In Progress - TCP1", qty=100, basic_rate=100, company="_Test Company with perpetual inventory") + + se2 = make_stock_entry(item_code="_Test Item Home Desktop 100", target="Work In Progress - TCP1", + qty=100, basic_rate=100, company="_Test Company with perpetual inventory") + pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=0, is_subcontracted="Yes", - company="_Test Company with perpetual inventory", warehouse='Stores - TCP1', supplier_warehouse='Work In Progress - TCP1') + company="_Test Company with perpetual inventory", warehouse='Stores - TCP1', + supplier_warehouse='Work In Progress - TCP1') gl_entries = get_gl_entries("Purchase Receipt", pr.name) self.assertFalse(gl_entries) - set_perpetual_inventory(0) + pr.cancel() + se1.cancel() + se2.cancel() def test_subcontracting_over_receipt(self): """ @@ -216,13 +227,13 @@ class TestPurchaseReceipt(unittest.TestCase): item_code = "_Test Subcontracted FG Item 1" make_subcontracted_item(item_code=item_code) - po = create_purchase_order(item_code=item_code, qty=1, + po = create_purchase_order(item_code=item_code, qty=1, include_exploded_items=0, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC") #stock raw materials in a warehouse before transfer - make_stock_entry(target="_Test Warehouse - _TC", - item_code = "Test Extra Item 1", qty=1, basic_rate=100) - make_stock_entry(target="_Test Warehouse - _TC", + se1 = make_stock_entry(target="_Test Warehouse - _TC", + item_code = "Test Extra Item 1", qty=10, basic_rate=100) + se2 = make_stock_entry(target="_Test Warehouse - _TC", item_code = "_Test FG Item", qty=1, basic_rate=100) rm_items = [ { @@ -254,6 +265,13 @@ class TestPurchaseReceipt(unittest.TestCase): pr1.submit() self.assertRaises(frappe.ValidationError, pr2.submit) + pr1.cancel() + se.cancel() + se1.cancel() + se2.cancel() + po.reload() + po.cancel() + def test_serial_no_supplier(self): pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1) self.assertEqual(frappe.db.get_value("Serial No", pr.get("items")[0].serial_no, "supplier"), @@ -284,6 +302,8 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(frappe.db.get_value("Serial No", serial_no, "warehouse"), pr.get("items")[0].rejected_warehouse) + pr.cancel() + 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") @@ -371,6 +391,9 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(pr.per_returned, 100) self.assertEqual(pr.status, 'Return Issued') + return_pr.cancel() + pr.cancel() + def test_purchase_return_for_rejected_qty(self): from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse @@ -388,6 +411,9 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(actual_qty, -2) + return_pr.cancel() + pr.cancel() + def test_purchase_return_for_serialized_items(self): def _check_serial_no_values(serial_no, field_values): @@ -415,6 +441,10 @@ class TestPurchaseReceipt(unittest.TestCase): "delivery_document_no": return_pr.name }) + return_pr.cancel() + pr.reload() + pr.cancel() + def test_purchase_return_for_multi_uom(self): item_code = "_Test Purchase Return For Multi-UOM" if not frappe.db.exists('Item', item_code): @@ -431,6 +461,9 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(abs(return_pr.items[0].stock_qty), 1.0) + return_pr.cancel() + pr.cancel() + def test_closed_purchase_receipt(self): from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_purchase_receipt_status @@ -440,6 +473,9 @@ class TestPurchaseReceipt(unittest.TestCase): update_purchase_receipt_status(pr.name, "Closed") self.assertEqual(frappe.db.get_value("Purchase Receipt", pr.name, "status"), "Closed") + pr.reload() + pr.cancel() + def test_pr_billing_status(self): # PO -> PR1 -> PI and PO -> PI and PO -> PR2 from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order @@ -482,6 +518,16 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(pr2.per_billed, 80) self.assertEqual(pr2.status, "To Bill") + pr2.cancel() + pi2.reload() + pi2.cancel() + pi1.reload() + pi1.cancel() + pr1.reload() + pr1.cancel() + po.reload() + po.cancel() + def test_serial_no_against_purchase_receipt(self): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -509,6 +555,8 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(serial_no, frappe.db.get_value("Serial No", {"purchase_document_type": "Purchase Receipt", "purchase_document_no": new_pr_doc.name}, "name")) + new_pr_doc.cancel() + def test_not_accept_duplicate_serial_no(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note @@ -519,16 +567,19 @@ class TestPurchaseReceipt(unittest.TestCase): item_code = item.name serial_no = random_string(5) - make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no) - create_delivery_note(item_code=item_code, qty=1, serial_no=serial_no) + pr1 = make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no) + dn = create_delivery_note(item_code=item_code, qty=1, serial_no=serial_no) - pr = make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no, do_not_submit=True) - self.assertRaises(SerialNoDuplicateError, pr.submit) + pr2 = make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no, do_not_submit=True) + self.assertRaises(SerialNoDuplicateError, pr2.submit) se = make_stock_entry(item_code=item_code, target="_Test Warehouse - _TC", qty=1, serial_no=serial_no, basic_rate=100, do_not_submit=True) self.assertRaises(SerialNoDuplicateError, se.submit) + dn.cancel() + pr1.cancel() + def test_auto_asset_creation(self): asset_item = "Test Asset Item" @@ -549,7 +600,7 @@ class TestPurchaseReceipt(unittest.TestCase): 'company_name': '_Test Company', 'fixed_asset_account': '_Test Fixed Asset - _TC', 'accumulated_depreciation_account': '_Test Accumulated Depreciations - _TC', - 'depreciation_expense_account': '_Test Depreciation - _TC' + 'depreciation_expense_account': '_Test Depreciations - _TC' }] }).insert() @@ -568,6 +619,8 @@ class TestPurchaseReceipt(unittest.TestCase): location = frappe.db.get_value('Asset', assets[0].name, 'location') self.assertEquals(location, "Test Location") + pr.cancel() + def test_purchase_return_with_submitted_asset(self): from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return @@ -594,6 +647,9 @@ class TestPurchaseReceipt(unittest.TestCase): pr_return.submit() + pr_return.cancel() + pr.cancel() + def test_purchase_receipt_cost_center(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center cost_center = "_Test Cost Center for BS Account - TCP1" @@ -605,7 +661,8 @@ class TestPurchaseReceipt(unittest.TestCase): 'location_name': 'Test Location' }).insert() - pr = make_purchase_receipt(cost_center=cost_center, company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1") + pr = make_purchase_receipt(cost_center=cost_center, company="_Test Company with perpetual inventory", + warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1") stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse) gl_entries = get_gl_entries("Purchase Receipt", pr.name) @@ -623,6 +680,8 @@ class TestPurchaseReceipt(unittest.TestCase): for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + pr.cancel() + def test_purchase_receipt_cost_center_with_balance_sheet_account(self): if not frappe.db.exists('Location', 'Test Location'): frappe.get_doc({ @@ -648,6 +707,8 @@ class TestPurchaseReceipt(unittest.TestCase): for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + pr.cancel() + def test_make_purchase_invoice_from_pr_for_returned_qty(self): from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order, create_pr_against_po @@ -663,6 +724,12 @@ class TestPurchaseReceipt(unittest.TestCase): pi = make_purchase_invoice(pr.name) self.assertEquals(pi.items[0].qty, 3) + pr1.cancel() + pr.reload() + pr.cancel() + po.reload() + po.cancel() + def test_make_purchase_invoice_from_pr_with_returned_qty_duplicate_items(self): pr1 = make_purchase_receipt(qty=8, do_not_submit=True) pr1.append("items", { @@ -689,8 +756,14 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEquals(pi2.items[0].qty, 2) self.assertEquals(pi2.items[1].qty, 1) + pr2.cancel() + pi1.cancel() + pr1.reload() + pr1.cancel() + def test_stock_transfer_from_purchase_receipt(self): - pr1 = make_purchase_receipt(warehouse = 'Work In Progress - TCP1', company="_Test Company with perpetual inventory") + pr1 = make_purchase_receipt(warehouse = 'Work In Progress - TCP1', + company="_Test Company with perpetual inventory") pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", do_not_save=1) @@ -713,18 +786,20 @@ class TestPurchaseReceipt(unittest.TestCase): for sle in sl_entries: self.assertEqual(expected_sle[sle.warehouse], sle.actual_qty) - def test_stock_transfer_from_purchase_receipt_with_valuation(self): - warehouse = frappe.get_doc('Warehouse', 'Work In Progress - TCP1') - warehouse.account = '_Test Account Stock In Hand - TCP1' - warehouse.save() + pr.cancel() + pr1.cancel() - pr1 = make_purchase_receipt(warehouse = 'Work In Progress - TCP1', + def test_stock_transfer_from_purchase_receipt_with_valuation(self): + create_warehouse("_Test Warehouse for Valuation", company="_Test Company with perpetual inventory", + properties={"account": '_Test Account Stock In Hand - TCP1'}) + + pr1 = make_purchase_receipt(warehouse = '_Test Warehouse for Valuation - TCP1', company="_Test Company with perpetual inventory") pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", do_not_save=1) - pr.items[0].from_warehouse = 'Work In Progress - TCP1' + pr.items[0].from_warehouse = '_Test Warehouse for Valuation - TCP1' pr.supplier_warehouse = '' @@ -749,7 +824,7 @@ class TestPurchaseReceipt(unittest.TestCase): ] expected_sle = { - 'Work In Progress - TCP1': -5, + '_Test Warehouse for Valuation - TCP1': -5, 'Stores - TCP1': 5 } @@ -761,60 +836,9 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(gle.debit, expected_gle[i][1]) self.assertEqual(gle.credit, expected_gle[i][2]) - warehouse.account = '' - warehouse.save() + pr.cancel() + pr1.cancel() - def test_backdated_purchase_receipt(self): - # make purchase receipt for default company - make_purchase_receipt(company="_Test Company 4", warehouse="Stores - _TC4") - - # try to make another backdated PR - posting_date = add_days(today(), -1) - pr = make_purchase_receipt(company="_Test Company 4", warehouse="Stores - _TC4", - do_not_submit=True) - - pr.set_posting_time = 1 - pr.posting_date = posting_date - pr.save() - - self.assertRaises(frappe.ValidationError, pr.submit) - - # make purchase receipt for other company backdated - pr = make_purchase_receipt(company="_Test Company 5", warehouse="Stores - _TC5", - do_not_submit=True) - - pr.set_posting_time = 1 - pr.posting_date = posting_date - pr.submit() - - # Allowed to submit for other company's PR - self.assertEqual(pr.docstatus, 1) - - def test_backdated_purchase_receipt_for_same_company_different_warehouse(self): - # make purchase receipt for default company - make_purchase_receipt(company="_Test Company 4", warehouse="Stores - _TC4") - - # try to make another backdated PR - posting_date = add_days(today(), -1) - pr = make_purchase_receipt(company="_Test Company 4", warehouse="Stores - _TC4", - do_not_submit=True) - - pr.set_posting_time = 1 - pr.posting_date = posting_date - pr.save() - - self.assertRaises(frappe.ValidationError, pr.submit) - - # make purchase receipt for other company backdated - pr = make_purchase_receipt(company="_Test Company 4", warehouse="Finished Goods - _TC4", - do_not_submit=True) - - pr.set_posting_time = 1 - pr.posting_date = posting_date - pr.submit() - - # Allowed to submit for other company's PR - self.assertEqual(pr.docstatus, 1) def test_subcontracted_pr_for_multi_transfer_batches(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry @@ -877,6 +901,12 @@ class TestPurchaseReceipt(unittest.TestCase): update_backflush_based_on("BOM") + pr.delete() + se.cancel() + ste2.cancel() + ste1.cancel() + po.cancel() + def get_sl_entries(voucher_type, voucher_no): return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s @@ -972,6 +1002,8 @@ def make_purchase_receipt(**args): pr.posting_date = args.posting_date or today() if args.posting_time: pr.posting_time = args.posting_time + if args.posting_date or args.posting_time: + pr.set_posting_time = 1 pr.company = args.company or "_Test Company" pr.supplier = args.supplier or "_Test Supplier" pr.is_subcontracted = args.is_subcontracted or "No" 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 84c64aa8f8..871b255b06 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -866,7 +866,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-11-02 10:00:38.204294", + "modified": "2020-12-07 10:00:38.204294", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/repost_item_valuation/__init__.py b/erpnext/stock/doctype/repost_item_valuation/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js new file mode 100644 index 0000000000..e429cd5e30 --- /dev/null +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js @@ -0,0 +1,52 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Repost Item Valuation', { + setup: function(frm) { + frm.set_query("warehouse", () => { + let filters = { + 'is_group': 0 + }; + if (frm.doc.company) filters['company'] = frm.doc.company; + return {filters: filters}; + }); + + frm.set_query("voucher_type", () => { + return { + filters: { + name: ['in', ['Purchase Receipt', 'Purchase Invoice', 'Delivery Note', + 'Sales Invoice', 'Stock Entry', 'Stock Reconciliation']] + } + }; + }); + + if (frm.doc.company) { + frm.set_query("voucher_no", () => { + return { + filters: { + company: frm.doc.company + } + }; + }); + } + }, + refresh: function(frm) { + if (frm.doc.status == "Failed") { + frm.add_custom_button(__('Restart'), function () { + frm.trigger("restart_reposting"); + }).addClass("btn-primary"); + } + }, + + restart_reposting: function(frm) { + frappe.call({ + method: "restart_reposting", + doc: frm.doc, + callback: function(r) { + if (!r.exc) { + frm.refresh(); + } + } + }); + } +}); diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json new file mode 100644 index 0000000000..071fc86d9b --- /dev/null +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json @@ -0,0 +1,215 @@ +{ + "actions": [], + "autoname": "REPOST-ITEM-VAL-.######", + "creation": "2020-10-22 22:27:07.742161", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "based_on", + "voucher_type", + "voucher_no", + "item_code", + "warehouse", + "posting_date", + "posting_time", + "column_break_5", + "status", + "company", + "allow_negative_stock", + "via_landed_cost_voucher", + "allow_zero_rate", + "amended_from", + "error_section", + "error_log" + ], + "fields": [ + { + "depends_on": "eval:doc.based_on=='Item and Warehouse'", + "fieldname": "item_code", + "fieldtype": "Link", + "label": "Item Code", + "mandatory_depends_on": "eval:doc.based_on=='Item and Warehouse'", + "options": "Item" + }, + { + "depends_on": "eval:doc.based_on=='Item and Warehouse'", + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "mandatory_depends_on": "eval:doc.based_on=='Item and Warehouse'", + "options": "Warehouse" + }, + { + "fetch_from": "voucher_no.posting_date", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "reqd": 1 + }, + { + "fetch_from": "voucher_no.posting_time", + "fieldname": "posting_time", + "fieldtype": "Time", + "label": "Posting Time" + }, + { + "default": "Queued", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "options": "Queued\nIn Progress\nCompleted\nFailed", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Repost Item Valuation", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:doc.status=='Failed'", + "fieldname": "error_section", + "fieldtype": "Section Break", + "label": "Error" + }, + { + "fieldname": "error_log", + "fieldtype": "Long Text", + "label": "Error Log", + "no_copy": 1, + "read_only": 1 + }, + { + "fetch_from": "warehouse.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "depends_on": "eval:doc.based_on=='Transaction'", + "fieldname": "voucher_type", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Voucher Type", + "mandatory_depends_on": "eval:doc.based_on=='Transaction'", + "options": "DocType" + }, + { + "depends_on": "eval:doc.based_on=='Transaction'", + "fieldname": "voucher_no", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Voucher No", + "mandatory_depends_on": "eval:doc.based_on=='Transaction'", + "options": "voucher_type" + }, + { + "default": "Transaction", + "fieldname": "based_on", + "fieldtype": "Select", + "label": "Based On", + "options": "Transaction\nItem and Warehouse", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "allow_negative_stock", + "fieldtype": "Check", + "label": "Allow Negative Stock" + }, + { + "default": "0", + "fieldname": "via_landed_cost_voucher", + "fieldtype": "Check", + "label": "Via Landed Cost Voucher" + }, + { + "default": "0", + "fieldname": "allow_zero_rate", + "fieldtype": "Check", + "label": "Allow Zero Rate" + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2020-12-10 07:52:12.476589", + "modified_by": "Administrator", + "module": "Stock", + "name": "Repost Item Valuation", + "owner": "Administrator", + "permissions": [ + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "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, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py new file mode 100644 index 0000000000..a942f2edda --- /dev/null +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -0,0 +1,89 @@ +# -*- 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, erpnext +from frappe.model.document import Document +from frappe.utils import cint +from erpnext.stock.stock_ledger import repost_future_sle +from erpnext.accounts.utils import update_gl_entries_after + + +class RepostItemValuation(Document): + def validate(self): + self.set_status() + self.reset_field_values() + self.set_company() + + def reset_field_values(self): + if self.based_on == 'Transaction': + self.item_code = None + self.warehouse = None + else: + self.voucher_type = None + self.voucher_no = None + + def set_company(self): + if self.voucher_type and self.voucher_no: + self.company = frappe.get_cached_value(self.voucher_type, self.voucher_no, "company") + elif self.warehouse: + self.company = frappe.get_cached_value("Warehouse", self.warehouse, "company") + + def set_status(self, status=None): + if not status: + status = 'Queued' + self.db_set('status', status) + + def on_submit(self): + frappe.enqueue(repost, timeout=1800, queue='long', + job_name='repost_sle', now=frappe.flags.in_test, doc=self) + + def restart_reposting(self): + self.set_status('Queued') + frappe.enqueue(repost, timeout=1800, queue='long', + job_name='repost_sle', now=True, doc=self) + +def repost(doc): + try: + doc.set_status('In Progress') + frappe.db.commit() + + repost_sl_entries(doc) + repost_gl_entries(doc) + doc.set_status('Completed') + except Exception: + frappe.db.rollback() + traceback = frappe.get_traceback() + frappe.log_error(traceback) + frappe.db.set_value(doc.doctype, doc.name, 'error_log', traceback) + doc.set_status('Failed') + raise + finally: + frappe.db.commit() + +def repost_sl_entries(doc): + if doc.based_on == 'Transaction': + repost_future_sle(voucher_type=doc.voucher_type, voucher_no=doc.voucher_no, + allow_negative_stock=doc.allow_negative_stock, via_landed_cost_voucher=doc.via_landed_cost_voucher) + else: + repost_future_sle(args=[frappe._dict({ + "item_code": doc.item_code, + "warehouse": doc.warehouse, + "posting_date": doc.posting_date, + "posting_time": doc.posting_time + })], allow_negative_stock=doc.allow_negative_stock, via_landed_cost_voucher=doc.via_landed_cost_voucher) + +def repost_gl_entries(doc): + if not cint(erpnext.is_perpetual_inventory_enabled(doc.company)): + return + + if doc.based_on == 'Transaction': + ref_doc = frappe.get_doc(doc.voucher_type, doc.voucher_no) + items, warehouses = ref_doc.get_items_and_warehouses() + else: + items = [doc.item_code] + warehouses = [doc.warehouse] + + update_gl_entries_after(doc.posting_date, doc.posting_time, + warehouses, items, company=doc.company) \ No newline at end of file diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py new file mode 100644 index 0000000000..13ceb68669 --- /dev/null +++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.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 TestRepostItemValuation(unittest.TestCase): + pass diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 295149e238..39ccf49c81 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -134,17 +134,13 @@ class SerialNo(StockController): sle_dict = self.get_stock_ledger_entries(serial_no) if sle_dict: if sle_dict.get("incoming", []): - sle_list = [sle for sle in sle_dict["incoming"] if sle.is_cancelled == 0] - if sle_list: - entries["purchase_sle"] = sle_list[0] + entries["purchase_sle"] = sle_dict["incoming"][0] if len(sle_dict.get("incoming", [])) - len(sle_dict.get("outgoing", [])) > 0: entries["last_sle"] = sle_dict["incoming"][0] else: entries["last_sle"] = sle_dict["outgoing"][0] - sle_list = [sle for sle in sle_dict["outgoing"] if sle.is_cancelled == 0] - if sle_list: - entries["delivery_sle"] = sle_list[0] + entries["delivery_sle"] = sle_dict["outgoing"][0] return entries @@ -155,11 +151,12 @@ class SerialNo(StockController): for sle in frappe.db.sql(""" SELECT voucher_type, voucher_no, - posting_date, posting_time, incoming_rate, actual_qty, serial_no, is_cancelled + posting_date, posting_time, incoming_rate, actual_qty, serial_no FROM `tabStock Ledger Entry` WHERE item_code=%s AND company = %s + AND is_cancelled = 0 AND (serial_no = %s OR serial_no like %s OR serial_no like %s @@ -179,7 +176,7 @@ class SerialNo(StockController): def on_trash(self): sl_entries = frappe.db.sql("""select serial_no from `tabStock Ledger Entry` - where serial_no like %s and item_code=%s""", + where serial_no like %s and item_code=%s and is_cancelled=0""", ("%%%s%%" % self.name, self.item_code), as_dict=True) # Find the exact match @@ -229,7 +226,7 @@ def validate_serial_no(sle, item_det): if serial_nos: frappe.throw(_("Item {0} is not setup for Serial Nos. Column must be blank").format(sle.item_code), SerialNoNotRequiredError) - else: + elif not sle.is_cancelled: if serial_nos: if cint(sle.actual_qty) != flt(sle.actual_qty): frappe.throw(_("Serial No {0} quantity {1} cannot be a fraction").format(sle.item_code, sle.actual_qty)) @@ -247,10 +244,6 @@ def validate_serial_no(sle, item_det): "delivery_document_no", "delivery_document_type", "warehouse", "purchase_document_no", "company"], as_dict=1) - if sr and cint(sle.actual_qty) < 0 and sr.warehouse != sle.warehouse: - frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}") - .format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse), SerialNoWarehouseError) - if sr.item_code!=sle.item_code: if not allow_serial_nos_with_different_item(serial_no, sle): frappe.throw(_("Serial No {0} does not belong to Item {1}").format(serial_no, @@ -277,7 +270,7 @@ def validate_serial_no(sle, item_det): frappe.throw(_("Serial No {0} does not belong to Batch {1}").format(serial_no, sle.batch_no), SerialNoBatchError) - if not sr.warehouse: + if not sle.is_cancelled and not sr.warehouse: frappe.throw(_("Serial No {0} does not belong to any Warehouse") .format(serial_no), SerialNoWarehouseError) @@ -327,6 +320,12 @@ def validate_serial_no(sle, item_det): elif cint(sle.actual_qty) < 0 or not item_det.serial_no_series: frappe.throw(_("Serial Nos Required for Serialized Item {0}").format(sle.item_code), SerialNoRequiredError) + elif serial_nos: + for serial_no in serial_nos: + sr = frappe.db.get_value("Serial No", serial_no, ["name", "warehouse"], as_dict=1) + if sr and cint(sle.actual_qty) < 0 and sr.warehouse != sle.warehouse: + frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}") + .format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse)) def validate_material_transfer_entry(sle_doc): sle_doc.update({ @@ -334,7 +333,7 @@ def validate_material_transfer_entry(sle_doc): "skip_serial_no_validaiton": False }) - if (sle_doc.voucher_type == "Stock Entry" and + if (sle_doc.voucher_type == "Stock Entry" and not sle_doc.is_cancelled and frappe.get_cached_value("Stock Entry", sle_doc.voucher_no, "purpose") == "Material Transfer"): if sle_doc.actual_qty < 0: sle_doc.skip_update_serial_no = True @@ -379,7 +378,7 @@ def allow_serial_nos_with_different_item(sle_serial_no, sle): stock_entry = frappe.get_cached_doc("Stock Entry", sle.voucher_no) if stock_entry.purpose in ("Repack", "Manufacture"): for d in stock_entry.get("items"): - if d.serial_no and (d.s_warehouse or d.t_warehouse): + if d.serial_no and (d.s_warehouse if not sle.is_cancelled else d.t_warehouse): serial_nos = get_serial_nos(d.serial_no) if sle_serial_no in serial_nos: allow_serial_nos = True @@ -388,7 +387,7 @@ def allow_serial_nos_with_different_item(sle_serial_no, sle): def update_serial_nos(sle, item_det): if sle.skip_update_serial_no: return - if not sle.serial_no and cint(sle.actual_qty) > 0 \ + if not sle.is_cancelled and not sle.serial_no and cint(sle.actual_qty) > 0 \ and item_det.has_serial_no == 1 and item_det.serial_no_series: serial_nos = get_auto_serial_nos(item_det.serial_no_series, sle.actual_qty) frappe.db.set(sle, "serial_no", serial_nos) diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py index ab061076e5..ed70790b2c 100644 --- a/erpnext/stock/doctype/serial_no/test_serial_no.py +++ b/erpnext/stock/doctype/serial_no/test_serial_no.py @@ -12,7 +12,6 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_pu from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse -from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory test_dependencies = ["Item"] test_records = frappe.get_test_records('Serial No') @@ -38,8 +37,6 @@ class TestSerialNo(unittest.TestCase): self.assertTrue(SerialNoCannotCannotChangeError, sr.save) def test_inter_company_transfer(self): - set_perpetual_inventory(0, "_Test Company 1") - set_perpetual_inventory(0) se = make_serialized_item(target_warehouse="_Test Warehouse - _TC") serial_nos = get_serial_nos(se.get("items")[0].serial_no) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 27fcbb7e2a..98116ec183 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -510,22 +510,31 @@ frappe.ui.form.on('Stock Entry', { calculate_amount: function(frm) { frm.events.calculate_total_additional_costs(frm); - - const total_basic_amount = frappe.utils.sum( - (frm.doc.items || []).map(function(i) { return i.t_warehouse ? flt(i.basic_amount) : 0; }) - ); - + let total_basic_amount = 0; + if (in_list(["Repack", "Manufacture"], frm.doc.purpose)) { + total_basic_amount = frappe.utils.sum( + (frm.doc.items || []).map(function(i) { + return i.is_finished_item ? flt(i.basic_amount) : 0; + }) + ); + } else { + total_basic_amount = frappe.utils.sum( + (frm.doc.items || []).map(function(i) { + return i.t_warehouse ? flt(i.basic_amount) : 0; + }) + ); + } + for (let i in frm.doc.items) { let item = frm.doc.items[i]; - if (item.t_warehouse && total_basic_amount) { + if (((in_list(["Repack", "Manufacture"], frm.doc.purpose) && item.is_finished_item) || item.t_warehouse) && total_basic_amount) { item.additional_cost = (flt(item.basic_amount) / total_basic_amount) * frm.doc.total_additional_costs; } else { item.additional_cost = 0; } - item.amount = flt(item.basic_amount + flt(item.additional_cost), - precision("amount", item)); + item.amount = flt(item.basic_amount + flt(item.additional_cost), precision("amount", item)); if (flt(item.transfer_qty)) { item.valuation_rate = flt(flt(item.basic_rate) + (flt(item.additional_cost) / flt(item.transfer_qty)), diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index 61e0df6723..5aed08102c 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -644,9 +644,10 @@ ], "icon": "fa fa-file-text", "idx": 1, + "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-08-11 19:10:07.954981", + "modified": "2020-09-09 12:59:02.508943", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 32d7e6eb34..afdb54ceaa 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -18,7 +18,7 @@ from erpnext.stock.utils import get_bin from frappe.model.mapper import get_mapped_doc from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit, get_serial_nos from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError - +from erpnext.accounts.general_ledger import process_gl_map import json from six import string_types, itervalues, iteritems @@ -58,6 +58,7 @@ class StockEntry(StockController): self.validate_warehouse() self.validate_work_order() self.validate_bom() + self.mark_finished_and_scrap_items() self.validate_finished_goods() self.validate_with_material_request() self.validate_batch() @@ -75,13 +76,11 @@ class StockEntry(StockController): else: set_batch_nos(self, 's_warehouse') - self.set_incoming_rate() self.validate_serialized_batch() self.set_actual_qty() - self.calculate_rate_and_amount(update_finished_item_rate=False) + self.calculate_rate_and_amount() def on_submit(self): - self.update_stock_ledger() update_serial_nos_after_submit(self, "items") @@ -89,11 +88,15 @@ class StockEntry(StockController): self.validate_purchase_order() if self.purchase_order and self.purpose == "Send to Subcontractor": self.update_purchase_order_supplied_items() + self.make_gl_entries() + + self.repost_future_sle_and_gle() self.update_cost_in_project() self.validate_reserved_serial_no_consumption() self.update_transferred_qty() self.update_quality_inspection() + if self.work_order and self.purpose == "Manufacture": self.update_so_in_serial_number() @@ -113,9 +116,10 @@ class StockEntry(StockController): self.update_work_order() self.update_stock_ledger() - self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') + self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation') self.make_gl_entries_on_cancel() + self.repost_future_sle_and_gle() self.update_cost_in_project() self.update_transferred_qty() self.update_quality_inspection() @@ -256,11 +260,10 @@ class StockEntry(StockController): def validate_fg_completed_qty(self): if self.purpose == "Manufacture" and self.work_order: - production_item = frappe.get_value('Work Order', self.work_order, 'production_item') - for item in self.items: - if item.item_code == production_item and item.t_warehouse and item.qty != self.fg_completed_qty: + for d in self.items: + if d.is_finished_item and d.qty != self.fg_completed_qty: frappe.throw(_("Finished product quantity {0} and For Quantity {1} cannot be different") - .format(item.qty, self.fg_completed_qty)) + .format(d.qty, self.fg_completed_qty)) def validate_difference_account(self): if not cint(erpnext.is_perpetual_inventory_enabled(self.company)): @@ -382,21 +385,6 @@ class StockEntry(StockController): frappe.throw(_("Stock Entries already created for Work Order ") + self.work_order + ":" + ", ".join(other_ste), DuplicateEntryForWorkOrderError) - def set_incoming_rate(self): - if self.purpose == "Repack": - self.set_basic_rate_for_finished_goods() - - for d in self.items: - if d.s_warehouse: - args = self.get_args_for_incoming_rate(d) - d.basic_rate = get_incoming_rate(args) - elif d.allow_zero_valuation_rate and not d.s_warehouse: - d.basic_rate = 0.0 - elif d.t_warehouse and not d.basic_rate: - d.basic_rate = get_valuation_rate(d.item_code, d.t_warehouse, - self.doctype, self.name, d.allow_zero_valuation_rate, - currency=erpnext.get_company_currency(self.company), company=self.company) - def set_actual_qty(self): allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")) @@ -432,57 +420,64 @@ class StockEntry(StockController): d.serial_no = transferred_serial_no def get_stock_and_rate(self): + """ + Updates rate and availability of all the items. + Called from Update Rate and Availability button. + """ self.set_work_order_details() self.set_transfer_qty() self.set_actual_qty() self.calculate_rate_and_amount() - def calculate_rate_and_amount(self, force=False, - update_finished_item_rate=True, raise_error_if_no_rate=True): - self.set_basic_rate(force, update_finished_item_rate, raise_error_if_no_rate) + def calculate_rate_and_amount(self, reset_outgoing_rate=True, raise_error_if_no_rate=True): + self.set_basic_rate(reset_outgoing_rate, raise_error_if_no_rate) self.distribute_additional_costs() self.update_valuation_rate() self.set_total_incoming_outgoing_value() self.set_total_amount() - def set_basic_rate(self, force=False, update_finished_item_rate=True, raise_error_if_no_rate=True): - """get stock and incoming rate on posting date""" - raw_material_cost = 0.0 - scrap_material_cost = 0.0 - fg_basic_rate = 0.0 + def set_basic_rate(self, reset_outgoing_rate=True, raise_error_if_no_rate=True): + """ + Set rate for outgoing, scrapped and finished items + """ + # Set rate for outgoing items + outgoing_items_cost = self.set_rate_for_outgoing_items(reset_outgoing_rate) + # Set basic rate for incoming items for d in self.get('items'): - if d.t_warehouse: fg_basic_rate = flt(d.basic_rate) - args = self.get_args_for_incoming_rate(d) + if d.s_warehouse or d.set_basic_rate_manually: continue - # get basic rate - if not d.bom_no: - if (not flt(d.basic_rate) and not d.allow_zero_valuation_rate) or d.s_warehouse or force: - basic_rate = flt(get_incoming_rate(args, raise_error_if_no_rate), self.precision("basic_rate", d)) - if basic_rate > 0: - d.basic_rate = basic_rate + if d.allow_zero_valuation_rate: + d.basic_rate = 0.0 + elif d.is_finished_item: + if self.purpose == "Manufacture": + d.basic_rate = self.get_basic_rate_for_manufactured_item(d.transfer_qty, outgoing_items_cost) + elif self.purpose == "Repack": + d.basic_rate = self.get_basic_rate_for_repacked_items(d.transfer_qty, outgoing_items_cost) + + if not d.basic_rate and not d.allow_zero_valuation_rate: + d.basic_rate = get_valuation_rate(d.item_code, d.t_warehouse, + self.doctype, self.name, d.allow_zero_valuation_rate, + currency=erpnext.get_company_currency(self.company), company=self.company, + raise_error_if_no_rate=raise_error_if_no_rate) + + d.basic_rate = flt(d.basic_rate, d.precision("basic_rate")) + d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) + + def set_rate_for_outgoing_items(self, reset_outgoing_rate=True): + outgoing_items_cost = 0.0 + for d in self.get('items'): + if d.s_warehouse: + if reset_outgoing_rate: + args = self.get_args_for_incoming_rate(d) + rate = get_incoming_rate(args) + if rate > 0: + d.basic_rate = rate d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) if not d.t_warehouse: - raw_material_cost += flt(d.basic_amount) - - # get scrap items basic rate - if d.bom_no: - if not flt(d.basic_rate) and not d.allow_zero_valuation_rate and \ - getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse: - basic_rate = flt(get_incoming_rate(args, raise_error_if_no_rate), - self.precision("basic_rate", d)) - if basic_rate > 0: - d.basic_rate = basic_rate - d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) - - if getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse: - - scrap_material_cost += flt(d.basic_amount) - - number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse]) - if (fg_basic_rate == 0.0 and number_of_fg_items == 1) or update_finished_item_rate: - self.set_basic_rate_for_finished_goods(raw_material_cost, scrap_material_cost) + outgoing_items_cost += flt(d.basic_amount) + return outgoing_items_cost def get_args_for_incoming_rate(self, item): return frappe._dict({ @@ -498,44 +493,44 @@ class StockEntry(StockController): "allow_zero_valuation": item.allow_zero_valuation_rate, }) - def set_basic_rate_for_finished_goods(self, raw_material_cost=0, scrap_material_cost=0): - total_fg_qty = 0 - if not raw_material_cost and self.get("items"): - raw_material_cost = sum([flt(row.basic_amount) for row in self.items - if row.s_warehouse and not row.t_warehouse]) + def get_basic_rate_for_repacked_items(self, finished_item_qty, outgoing_items_cost): + finished_items = [d.item_code for d in self.get("items") if d.is_finished_item] + if len(finished_items) == 1: + return flt(outgoing_items_cost / finished_item_qty) + else: + unique_finished_items = set(finished_items) + if len(unique_finished_items) == 1: + total_fg_qty = sum([flt(d.transfer_qty) for d in self.items if d.is_finished_item]) + return flt(outgoing_items_cost / total_fg_qty) - total_fg_qty = sum([flt(row.qty) for row in self.items - if row.t_warehouse and not row.s_warehouse]) + def get_basic_rate_for_manufactured_item(self, finished_item_qty, outgoing_items_cost=0): + scrap_items_cost = sum([flt(d.basic_amount) for d in self.get("items") if d.is_scrap_item]) - if self.purpose in ["Manufacture", "Repack"]: - for d in self.get("items"): - if (d.transfer_qty and (d.bom_no or d.t_warehouse) - and (getattr(self, "pro_doc", frappe._dict()).scrap_warehouse != d.t_warehouse)): + # Get raw materials cost from BOM if multiple material consumption entries + if frappe.db.get_single_value("Manufacturing Settings", "material_consumption"): + bom_items = self.get_bom_raw_materials(finished_item_qty) + outgoing_items_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()]) - if (self.work_order and self.purpose == "Manufacture" - and frappe.db.get_single_value("Manufacturing Settings", "material_consumption")): - bom_items = self.get_bom_raw_materials(d.transfer_qty) - raw_material_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()]) - - if raw_material_cost and self.purpose == "Manufacture": - d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate")) - d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount")) - elif self.purpose == "Repack" and total_fg_qty and not d.set_basic_rate_manually: - d.basic_rate = flt(raw_material_cost) / flt(total_fg_qty) - d.basic_amount = d.basic_rate * flt(d.qty) + return flt(outgoing_items_cost - scrap_items_cost) def distribute_additional_costs(self): - if self.purpose == "Material Issue": + # If no incoming items, set additional costs blank + if not any([d.item_code for d in self.items if d.t_warehouse]): self.additional_costs = [] self.total_additional_costs = sum([flt(t.amount) for t in self.get("additional_costs")]) - total_basic_amount = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse]) - for d in self.get("items"): - if d.t_warehouse and total_basic_amount: - d.additional_cost = (flt(d.basic_amount) / total_basic_amount) * self.total_additional_costs - else: - d.additional_cost = 0 + if self.purpose in ("Repack", "Manufacture"): + incoming_items_cost = sum([flt(t.basic_amount) for t in self.get("items") if t.is_finished_item]) + else: + incoming_items_cost = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse]) + + if incoming_items_cost: + for d in self.get("items"): + if (self.purpose in ("Repack", "Manufacture") and d.is_finished_item) or d.t_warehouse: + d.additional_cost = (flt(d.basic_amount) / incoming_items_cost) * self.total_additional_costs + else: + d.additional_cost = 0 def update_valuation_rate(self): for d in self.get("items"): @@ -638,71 +633,115 @@ class StockEntry(StockController): item_code = d.original_item or d.item_code validate_bom_no(item_code, d.bom_no) + def mark_finished_and_scrap_items(self): + if self.purpose in ("Repack", "Manufacture"): + if any([d.item_code for d in self.items if (d.is_finished_item and d.t_warehouse)]): + return + + finished_item = self.get_finished_item() + + for d in self.items: + if d.t_warehouse and not d.s_warehouse: + if self.purpose=="Repack" or d.item_code == finished_item: + d.is_finished_item = 1 + else: + d.is_scrap_item = 1 + else: + d.is_finished_item = 0 + d.is_scrap_item = 0 + + def get_finished_item(self): + finished_item = None + if self.work_order: + finished_item = frappe.db.get_value("Work Order", self.work_order, "production_item") + elif self.bom_no: + finished_item = frappe.db.get_value("BOM", self.bom_no, "item") + + return finished_item + def validate_finished_goods(self): """validation: finished good quantity should be same as manufacturing quantity""" if not self.work_order: return - items_with_target_warehouse = [] - allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings", - "overproduction_percentage_for_work_order")) - production_item, wo_qty = frappe.db.get_value("Work Order", self.work_order, ["production_item", "qty"]) + number_of_finished_items = 0 for d in self.get('items'): - if (self.purpose != "Send to Subcontractor" and d.bom_no - and flt(d.transfer_qty) > flt(self.fg_completed_qty) and d.item_code == production_item): - frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \ - format(d.idx, d.transfer_qty, self.fg_completed_qty)) + if d.is_finished_item: + if d.item_code != production_item: + frappe.throw(_("Finished Item {0} does not match with Work Order {1}") + .format(d.item_code, self.work_order)) + elif flt(d.transfer_qty) > flt(self.fg_completed_qty): + frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \ + format(d.idx, d.transfer_qty, self.fg_completed_qty)) + number_of_finished_items += 1 - if self.work_order and self.purpose == "Manufacture" and d.t_warehouse: - items_with_target_warehouse.append(d.item_code) + if number_of_finished_items > 1: + frappe.throw(_("Multiple items cannot be marked as finished item")) + + if self.purpose == "Manufacture": + allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings", + "overproduction_percentage_for_work_order")) - if self.work_order and self.purpose == "Manufacture": allowed_qty = wo_qty + (allowance_percentage/100 * wo_qty) if self.fg_completed_qty > allowed_qty: frappe.throw(_("For quantity {0} should not be greater than work order quantity {1}") .format(flt(self.fg_completed_qty), wo_qty)) - if production_item not in items_with_target_warehouse: - frappe.throw(_("Finished Item {0} must be entered for Manufacture type entry") - .format(production_item)) - def update_stock_ledger(self): sl_entries = [] + finished_item_row = self.get_finished_item_row() - # make sl entries for source warehouse first, then do for target warehouse - for d in self.get('items'): - if cstr(d.s_warehouse): - sl_entries.append(self.get_sl_entries(d, { - "warehouse": cstr(d.s_warehouse), - "actual_qty": -flt(d.transfer_qty), - "incoming_rate": 0 - })) - - for d in self.get('items'): - if cstr(d.t_warehouse): - sl_entries.append(self.get_sl_entries(d, { - "warehouse": cstr(d.t_warehouse), - "actual_qty": flt(d.transfer_qty), - "incoming_rate": flt(d.valuation_rate) - })) - - # On cancellation, make stock ledger entry for - # target warehouse first, to update serial no values properly - - # if cstr(d.s_warehouse) and self.docstatus == 2: - # sl_entries.append(self.get_sl_entries(d, { - # "warehouse": cstr(d.s_warehouse), - # "actual_qty": -flt(d.transfer_qty), - # "incoming_rate": 0 - # })) + # make sl entries for source warehouse first + self.get_sle_for_source_warehouse(sl_entries, finished_item_row) + # SLE for target warehouse + self.get_sle_for_target_warehouse(sl_entries, finished_item_row) + + # reverse sl entries if cancel if self.docstatus == 2: sl_entries.reverse() self.make_sl_entries(sl_entries) + def get_finished_item_row(self): + finished_item_row = None + if self.purpose in ("Manufacture", "Repack"): + for d in self.get('items'): + if d.is_finished_item: + finished_item_row = d + + return finished_item_row + + def get_sle_for_source_warehouse(self, sl_entries, finished_item_row): + for d in self.get('items'): + if cstr(d.s_warehouse): + sle = self.get_sl_entries(d, { + "warehouse": cstr(d.s_warehouse), + "actual_qty": -flt(d.transfer_qty), + "incoming_rate": 0 + }) + if cstr(d.t_warehouse): + sle.dependant_sle_voucher_detail_no = d.name + elif finished_item_row and (finished_item_row.item_code != d.item_code or finished_item_row.t_warehouse != d.s_warehouse): + sle.dependant_sle_voucher_detail_no = finished_item_row.name + + sl_entries.append(sle) + + def get_sle_for_target_warehouse(self, sl_entries, finished_item_row): + for d in self.get('items'): + if cstr(d.t_warehouse): + sle = self.get_sl_entries(d, { + "warehouse": cstr(d.t_warehouse), + "actual_qty": flt(d.transfer_qty), + "incoming_rate": flt(d.valuation_rate) + }) + if cstr(d.s_warehouse) or (finished_item_row and d.name == finished_item_row.name): + sle.recalculate_rate = 1 + + sl_entries.append(sle) + def get_gl_entries(self, warehouse_account): gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account) @@ -747,7 +786,7 @@ class StockEntry(StockController): "credit": -1 * amount # put it as negative credit instead of debit purposefully }, item=d)) - return gl_entries + return process_gl_map(gl_entries) def update_work_order(self): def _validate_work_order(pro_doc): @@ -996,6 +1035,7 @@ class StockEntry(StockController): "stock_uom": item.stock_uom, "expense_account": item.get("expense_account"), "cost_center": item.get("buying_cost_center"), + "is_finished_item": 1 } }, bom_no = self.bom_no) @@ -1034,6 +1074,7 @@ class StockEntry(StockController): for item in itervalues(item_dict): item.from_warehouse = "" + item.is_scrap_item = 1 return item_dict def get_unconsumed_raw_materials(self): @@ -1246,6 +1287,8 @@ class StockEntry(StockController): 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)) + se_child.is_finished_item = item_dict[d].get("is_finished_item", 0) + se_child.is_scrap_item = item_dict[d].get("is_scrap_item", 0) for field in ["idx", "po_detail", "original_item", "expense_account", "description", "item_name"]: diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 9b6744ca3c..1a641855aa 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -6,7 +6,6 @@ import frappe, unittest import frappe.defaults from frappe.utils import flt, nowdate, nowtime from erpnext.stock.doctype.serial_no.serial_no import * -from erpnext import set_perpetual_inventory from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError from erpnext.stock.stock_ledger import get_previous_sle from frappe.permissions import add_user_permission, remove_user_permission @@ -32,7 +31,6 @@ def get_sle(**args): class TestStockEntry(unittest.TestCase): def tearDown(self): frappe.set_user("Administrator") - set_perpetual_inventory(0) def test_fifo(self): frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) @@ -213,7 +211,6 @@ class TestStockEntry(unittest.TestCase): def test_repack_no_change_in_valuation(self): company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') - set_perpetual_inventory(0, company) make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100) make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", @@ -235,8 +232,6 @@ class TestStockEntry(unittest.TestCase): order by account desc""", repack.name, as_dict=1) self.assertFalse(gl_entries) - set_perpetual_inventory(0, repack.company) - def test_repack_with_additional_costs(self): company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company') @@ -474,7 +469,6 @@ class TestStockEntry(unittest.TestCase): def test_warehouse_company_validation(self): company = frappe.db.get_value('Warehouse', '_Test Warehouse 2 - _TC1', 'company') - set_perpetual_inventory(0, company) frappe.get_doc("User", "test2@example.com")\ .add_roles("Sales User", "Sales Manager", "Stock User", "Stock Manager") frappe.set_user("test2@example.com") @@ -500,7 +494,7 @@ class TestStockEntry(unittest.TestCase): st1 = frappe.copy_doc(test_records[0]) st1.company = "_Test Company 1" - set_perpetual_inventory(0, st1.company) + frappe.set_user("test@example.com") st1.get("items")[0].t_warehouse="_Test Warehouse 2 - _TC1" self.assertRaises(frappe.PermissionError, st1.insert) @@ -698,47 +692,54 @@ class TestStockEntry(unittest.TestCase): repack.insert() self.assertRaises(frappe.ValidationError, repack.submit) - def test_material_consumption(self): - from erpnext.manufacturing.doctype.work_order.work_order \ - import make_stock_entry as _make_stock_entry - bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item 2", - "is_default": 1, "docstatus": 1}) + # def test_material_consumption(self): + # frappe.db.set_value("Manufacturing Settings", None, "backflush_raw_materials_based_on", "BOM") + # frappe.db.set_value("Manufacturing Settings", None, "material_consumption", "0") - work_order = frappe.new_doc("Work Order") - work_order.update({ - "company": "_Test Company", - "fg_warehouse": "_Test Warehouse 1 - _TC", - "production_item": "_Test FG Item 2", - "bom_no": bom_no, - "qty": 4.0, - "stock_uom": "_Test UOM", - "wip_warehouse": "_Test Warehouse - _TC", - "additional_operating_cost": 1000 - }) - work_order.insert() - work_order.submit() + # from erpnext.manufacturing.doctype.work_order.work_order \ + # import make_stock_entry as _make_stock_entry + # bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item 2", + # "is_default": 1, "docstatus": 1}) - make_stock_entry(item_code="_Test Serialized Item With Series", target="_Test Warehouse - _TC", qty=50, basic_rate=100) - make_stock_entry(item_code="_Test Item 2", target="_Test Warehouse - _TC", qty=50, basic_rate=20) + # work_order = frappe.new_doc("Work Order") + # work_order.update({ + # "company": "_Test Company", + # "fg_warehouse": "_Test Warehouse 1 - _TC", + # "production_item": "_Test FG Item 2", + # "bom_no": bom_no, + # "qty": 4.0, + # "stock_uom": "_Test UOM", + # "wip_warehouse": "_Test Warehouse - _TC", + # "additional_operating_cost": 1000, + # "use_multi_level_bom": 1 + # }) + # work_order.insert() + # work_order.submit() - item_quantity = { - '_Test Item': 10.0, - '_Test Item 2': 12.0, - '_Test Serialized Item With Series': 6.0 - } + # make_stock_entry(item_code="_Test Serialized Item With Series", target="_Test Warehouse - _TC", qty=50, basic_rate=100) + # make_stock_entry(item_code="_Test Item 2", target="_Test Warehouse - _TC", qty=50, basic_rate=20) - stock_entry = frappe.get_doc(_make_stock_entry(work_order.name, "Material Consumption for Manufacture", 2)) - for d in stock_entry.get('items'): - self.assertEqual(item_quantity.get(d.item_code), d.qty) + # item_quantity = { + # '_Test Item': 2.0, + # '_Test Item 2': 12.0, + # '_Test Serialized Item With Series': 6.0 + # } + + # stock_entry = frappe.get_doc(_make_stock_entry(work_order.name, "Material Consumption for Manufacture", 2)) + # for d in stock_entry.get('items'): + # self.assertEqual(item_quantity.get(d.item_code), d.qty) def test_customer_provided_parts_se(self): create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0) - se = make_stock_entry(item_code='CUST-0987', purpose = 'Material Receipt', qty=4, to_warehouse = "_Test Warehouse - _TC") + se = make_stock_entry(item_code='CUST-0987', purpose = 'Material Receipt', + qty=4, to_warehouse = "_Test Warehouse - _TC") self.assertEqual(se.get("items")[0].allow_zero_valuation_rate, 1) self.assertEqual(se.get("items")[0].amount, 0) def test_gle_for_opening_stock_entry(self): - mr = make_stock_entry(item_code="_Test Item", target="Stores - TCP1", company="_Test Company with perpetual inventory",qty=50, basic_rate=100, expense_account="Stock Adjustment - TCP1", is_opening="Yes", do_not_save=True) + mr = make_stock_entry(item_code="_Test Item", target="Stores - TCP1", + company="_Test Company with perpetual inventory", qty=50, basic_rate=100, + expense_account="Stock Adjustment - TCP1", is_opening="Yes", do_not_save=True) self.assertRaises(OpeningEntryAccountError, mr.save) @@ -759,8 +760,8 @@ class TestStockEntry(unittest.TestCase): "company":"_Test Company with perpetual inventory", "items":[ { - "item_code":"Basil Leaves", - "description":"Basil Leaves", + "item_code":"_Test Item", + "description":"_Test Item", "qty": 1, "basic_rate": 0, "uom":"Nos", @@ -769,8 +770,8 @@ class TestStockEntry(unittest.TestCase): "cost_center": "Main - TCP1" }, { - "item_code":"Basil Leaves", - "description":"Basil Leaves", + "item_code":"_Test Item", + "description":"_Test Item", "qty": 2, "basic_rate": 0, "uom":"Nos", diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 79e8f9af8f..6fe60298ee 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -13,8 +13,10 @@ "t_warehouse", "sec_break1", "item_code", - "col_break2", "item_name", + "col_break2", + "is_finished_item", + "is_scrap_item", "subcontracted_item", "section_break_8", "description", @@ -22,35 +24,37 @@ "item_group", "image", "image_view", - "quantity_and_rate", - "set_basic_rate_manually", + "quantity_section", "qty", - "basic_rate", - "basic_amount", - "additional_cost", - "amount", - "valuation_rate", - "col_break3", - "uom", - "conversion_factor", - "stock_uom", "transfer_qty", "retain_sample", + "column_break_20", + "uom", + "stock_uom", + "conversion_factor", "sample_quantity", + "rates_section", + "basic_rate", + "additional_cost", + "valuation_rate", + "allow_zero_valuation_rate", + "col_break3", + "set_basic_rate_manually", + "basic_amount", + "amount", "serial_no_batch", "serial_no", "col_break4", "batch_no", - "quality_inspection", "accounting", "expense_account", - "col_break5", "accounting_dimensions_section", "cost_center", + "project", "dimension_col_break", "more_info", - "allow_zero_valuation_rate", "actual_qty", + "transferred_qty", "bom_no", "allow_alternative_item", "col_break6", @@ -62,9 +66,8 @@ "ste_detail", "po_detail", "column_break_51", - "transferred_qty", "reference_purchase_receipt", - "project" + "quality_inspection" ], "fields": [ { @@ -159,11 +162,6 @@ "options": "image", "print_hide": 1 }, - { - "fieldname": "quantity_and_rate", - "fieldtype": "Section Break", - "label": "Quantity and Rate" - }, { "bold": 1, "fieldname": "qty", @@ -321,10 +319,6 @@ "options": "Account", "print_hide": 1 }, - { - "fieldname": "col_break5", - "fieldtype": "Column Break" - }, { "default": ":Company", "depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))", @@ -335,6 +329,7 @@ "print_hide": 1 }, { + "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", "label": "More Information" @@ -456,6 +451,7 @@ "read_only": 1 }, { + "collapsible": 1, "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", "label": "Accounting Dimensions" @@ -498,6 +494,32 @@ "fieldname": "set_basic_rate_manually", "fieldtype": "Check", "label": "Set Basic Rate Manually" + }, + { + "fieldname": "quantity_section", + "fieldtype": "Section Break", + "label": "Quantity" + }, + { + "fieldname": "column_break_20", + "fieldtype": "Column Break" + }, + { + "fieldname": "rates_section", + "fieldtype": "Section Break", + "label": "Rates" + }, + { + "default": "0", + "fieldname": "is_scrap_item", + "fieldtype": "Check", + "label": "Is Scrap Item" + }, + { + "default": "0", + "fieldname": "is_finished_item", + "fieldtype": "Check", + "label": "Is Finished Item" } ], "idx": 1, diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json index fda17e08ab..2463a21ed6 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json @@ -8,26 +8,33 @@ "engine": "InnoDB", "field_order": [ "item_code", - "serial_no", - "batch_no", "warehouse", "posting_date", "posting_time", + "column_break_6", "voucher_type", "voucher_no", "voucher_detail_no", + "dependant_sle_voucher_detail_no", + "recalculate_rate", + "section_break_11", "actual_qty", + "qty_after_transaction", "incoming_rate", "outgoing_rate", - "stock_uom", - "qty_after_transaction", + "column_break_17", "valuation_rate", "stock_value", "stock_value_difference", "stock_queue", - "project", + "section_break_21", "company", + "stock_uom", + "project", + "batch_no", + "column_break_26", "fiscal_year", + "serial_no", "is_cancelled", "to_rename" ], @@ -50,7 +57,6 @@ { "fieldname": "serial_no", "fieldtype": "Long Text", - "in_list_view": 1, "label": "Serial No", "print_width": "100px", "read_only": 1, @@ -59,7 +65,6 @@ { "fieldname": "batch_no", "fieldtype": "Data", - "in_list_view": 1, "label": "Batch No", "oldfieldname": "batch_no", "oldfieldtype": "Data", @@ -119,6 +124,7 @@ "fieldname": "voucher_no", "fieldtype": "Dynamic Link", "in_filter": 1, + "in_list_view": 1, "in_standard_filter": 1, "label": "Voucher No", "oldfieldname": "voucher_no", @@ -142,6 +148,7 @@ "fieldname": "actual_qty", "fieldtype": "Float", "in_filter": 1, + "in_list_view": 1, "label": "Actual Quantity", "oldfieldname": "actual_qty", "oldfieldtype": "Currency", @@ -152,6 +159,7 @@ { "fieldname": "incoming_rate", "fieldtype": "Currency", + "in_list_view": 1, "label": "Incoming Rate", "oldfieldname": "incoming_rate", "oldfieldtype": "Currency", @@ -217,13 +225,11 @@ { "fieldname": "stock_queue", "fieldtype": "Text", - "hidden": 1, "label": "Stock Queue (FIFO)", "oldfieldname": "fcfs_stack", "oldfieldtype": "Text", "print_hide": 1, - "read_only": 1, - "report_hide": 1 + "read_only": 1 }, { "fieldname": "project", @@ -269,14 +275,48 @@ "hidden": 1, "label": "To Rename", "search_index": 1 + }, + { + "fieldname": "dependant_sle_voucher_detail_no", + "fieldtype": "Data", + "label": "Dependant SLE Voucher Detail No" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_11", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_21", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "recalculate_rate", + "fieldtype": "Check", + "label": "Recalculate Incoming/Outgoing Rate", + "no_copy": 1, + "read_only": 1 } ], "hide_toolbar": 1, "icon": "fa fa-list", "idx": 1, "in_create": 1, + "index_web_pages_for_search": 1, "links": [], - "modified": "2020-04-23 05:57:03.985520", + "modified": "2020-09-07 11:10:35.318872", "modified_by": "Administrator", "module": "Stock", "name": "Stock Ledger Entry", diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index bb356f694a..a5c303ccb4 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -10,8 +10,10 @@ from frappe.model.document import Document from datetime import date from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock from erpnext.accounts.utils import get_fiscal_year +from frappe.core.doctype.role.role import get_users class StockFreezeError(frappe.ValidationError): pass +class BackDatedStockTransaction(frappe.ValidationError): pass exclude_from_linked_with = True @@ -34,7 +36,6 @@ class StockLedgerEntry(Document): self.validate_and_set_fiscal_year() self.block_transactions_against_group_warehouse() self.validate_with_last_transaction_posting_time() - self.validate_future_posting() def on_submit(self): self.check_stock_frozen_date() @@ -48,7 +49,7 @@ class StockLedgerEntry(Document): def calculate_batch_qty(self): if self.batch_no: batch_qty = frappe.db.get_value("Stock Ledger Entry", - {"docstatus": 1, "batch_no": self.batch_no}, + {"docstatus": 1, "batch_no": self.batch_no, "is_cancelled": 0}, "sum(actual_qty)") or 0 frappe.db.set_value("Batch", self.batch_no, "batch_qty", batch_qty) @@ -88,14 +89,14 @@ class StockLedgerEntry(Document): # check if batch number is required if self.voucher_type != 'Stock Reconciliation': - if item_det.has_batch_no ==1: + if item_det.has_batch_no == 1: batch_item = self.item_code if self.item_code == item_det.item_name else self.item_code + ":" + item_det.item_name if not self.batch_no: frappe.throw(_("Batch number is mandatory for Item {0}").format(batch_item)) elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}): frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, batch_item)) - elif item_det.has_batch_no ==0 and self.batch_no: + elif item_det.has_batch_no == 0 and self.batch_no and self.is_cancelled == 0: frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code)) if item_det.has_variants: @@ -142,28 +143,28 @@ class StockLedgerEntry(Document): is_group_warehouse(self.warehouse) def validate_with_last_transaction_posting_time(self): - last_transaction_time = frappe.db.sql(""" - select MAX(timestamp(posting_date, posting_time)) as posting_time - from `tabStock Ledger Entry` - where docstatus = 1 and item_code = %s - and warehouse = %s""", (self.item_code, self.warehouse))[0][0] + authorized_role = frappe.db.get_single_value("Stock Settings", "role_allowed_to_create_edit_back_dated_transactions") + if authorized_role: + authorized_users = get_users(authorized_role) + if authorized_users and frappe.session.user not in authorized_users: + last_transaction_time = frappe.db.sql(""" + select MAX(timestamp(posting_date, posting_time)) as posting_time + from `tabStock Ledger Entry` + where docstatus = 1 and item_code = %s + and warehouse = %s""", (self.item_code, self.warehouse))[0][0] - cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00") + cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00") - if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time): - msg = _("Last Stock Transaction for item {0} under warehouse {1} was on {2}.").format(frappe.bold(self.item_code), - frappe.bold(self.warehouse), frappe.bold(last_transaction_time)) + if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time): + msg = _("Last Stock Transaction for item {0} under warehouse {1} was on {2}.").format(frappe.bold(self.item_code), + frappe.bold(self.warehouse), frappe.bold(last_transaction_time)) - msg += "

    " + _("Stock Transactions for Item {0} under warehouse {1} cannot be posted before this time.").format( - frappe.bold(self.item_code), frappe.bold(self.warehouse)) + msg += "

    " + _("You are not authorized to make/edit Stock Transactions for Item {0} under warehouse {1} before this time.").format( + frappe.bold(self.item_code), frappe.bold(self.warehouse)) - msg += "

    " + _("Please remove this item and try to submit again or update the posting time.") - frappe.throw(msg, title=_("Backdated Stock Entry")) - - def validate_future_posting(self): - if date_diff(self.posting_date, getdate()) > 0: - msg = _("Posting future stock transactions are not allowed due to Immutable Ledger") - frappe.throw(msg, title=_("Future Posting Not Allowed")) + msg += "

    " + _("Please contact any of the following users to {} this transaction.") + msg += "
    " + "
    ".join(authorized_users) + frappe.throw(msg, BackDatedStockTransaction, title=_("Backdated Stock Entry")) def on_doctype_update(): if not frappe.db.has_index('tabStock Ledger Entry', 'posting_sort_index'): diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index 04dae83447..59f1f3961b 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -5,8 +5,397 @@ from __future__ import unicode_literals import frappe import unittest - -# test_records = frappe.get_test_records('Stock Ledger Entry') +from frappe.utils import today, add_days +from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry +from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation \ + import create_stock_reconciliation +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.stock.stock_ledger import get_previous_sle +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt +from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import create_landed_cost_voucher +from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note +from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import BackDatedStockTransaction class TestStockLedgerEntry(unittest.TestCase): - pass + def setUp(self): + items = create_items() + + # delete SLE and BINs for all items + frappe.db.sql("delete from `tabStock Ledger Entry` where item_code in (%s)" % (', '.join(['%s']*len(items))), items) + frappe.db.sql("delete from `tabBin` where item_code in (%s)" % (', '.join(['%s']*len(items))), items) + + def test_item_cost_reposting(self): + company = "_Test Company" + + # _Test Item for Reposting at Stores warehouse on 10-04-2020: Qty = 50, Rate = 100 + create_stock_reconciliation( + item_code="_Test Item for Reposting", + warehouse="Stores - _TC", + qty=50, + rate=100, + company=company, + expense_account = "Stock Adjustment - _TC", + posting_date='2020-04-10', + posting_time='14:00' + ) + + # _Test Item for Reposting at FG warehouse on 20-04-2020: Qty = 10, Rate = 200 + create_stock_reconciliation( + item_code="_Test Item for Reposting", + warehouse="Finished Goods - _TC", + qty=10, + rate=200, + company=company, + expense_account = "Stock Adjustment - _TC", + posting_date='2020-04-20', + posting_time='14:00' + ) + + # _Test Item for Reposting transferred from Stores to FG warehouse on 30-04-2020 + make_stock_entry( + item_code="_Test Item for Reposting", + source="Stores - _TC", + target="Finished Goods - _TC", + company=company, + qty=10, + expense_account="Stock Adjustment - _TC", + posting_date='2020-04-30', + posting_time='14:00' + ) + target_wh_sle = get_previous_sle({ + "item_code": "_Test Item for Reposting", + "warehouse": "Finished Goods - _TC", + "posting_date": '2020-04-30', + "posting_time": '14:00' + }) + + self.assertEqual(target_wh_sle.get("valuation_rate"), 150) + + # Repack entry on 5-5-2020 + repack = create_repack_entry(company=company, posting_date='2020-05-05', posting_time='14:00') + + finished_item_sle = get_previous_sle({ + "item_code": "_Test Finished Item for Reposting", + "warehouse": "Finished Goods - _TC", + "posting_date": '2020-05-05', + "posting_time": '14:00' + }) + self.assertEqual(finished_item_sle.get("incoming_rate"), 540) + self.assertEqual(finished_item_sle.get("valuation_rate"), 540) + + # Reconciliation for _Test Item for Reposting at Stores on 12-04-2020: Qty = 50, Rate = 150 + create_stock_reconciliation( + item_code="_Test Item for Reposting", + warehouse="Stores - _TC", + qty=50, + rate=150, + company=company, + expense_account = "Stock Adjustment - _TC", + posting_date='2020-04-12', + posting_time='14:00' + ) + + + # Check valuation rate of finished goods warehouse after back-dated entry at Stores + target_wh_sle = get_previous_sle({ + "item_code": "_Test Item for Reposting", + "warehouse": "Finished Goods - _TC", + "posting_date": '2020-04-30', + "posting_time": '14:00' + }) + self.assertEqual(target_wh_sle.get("incoming_rate"), 150) + self.assertEqual(target_wh_sle.get("valuation_rate"), 175) + + # Check valuation rate of repacked item after back-dated entry at Stores + finished_item_sle = get_previous_sle({ + "item_code": "_Test Finished Item for Reposting", + "warehouse": "Finished Goods - _TC", + "posting_date": '2020-05-05', + "posting_time": '14:00' + }) + self.assertEqual(finished_item_sle.get("incoming_rate"), 790) + self.assertEqual(finished_item_sle.get("valuation_rate"), 790) + + # Check updated rate in Repack entry + repack.reload() + self.assertEqual(repack.items[0].get("basic_rate"), 150) + self.assertEqual(repack.items[1].get("basic_rate"), 750) + + def test_purchase_return_valuation_reposting(self): + pr = make_purchase_receipt(company="_Test Company", posting_date='2020-04-10', + warehouse="Stores - _TC", item_code="_Test Item for Reposting", qty=5, rate=100) + + return_pr = make_purchase_receipt(company="_Test Company", posting_date='2020-04-15', + warehouse="Stores - _TC", item_code="_Test Item for Reposting", is_return=1, return_against=pr.name, qty=-2) + + # check sle + outgoing_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt", + "voucher_no": return_pr.name}, ["outgoing_rate", "stock_value_difference"]) + + self.assertEqual(outgoing_rate, 100) + self.assertEqual(stock_value_difference, -200) + + create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) + + outgoing_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt", + "voucher_no": return_pr.name}, ["outgoing_rate", "stock_value_difference"]) + + self.assertEqual(outgoing_rate, 110) + self.assertEqual(stock_value_difference, -220) + + def test_sales_return_valuation_reposting(self): + company = "_Test Company" + item_code="_Test Item for Reposting" + + # Purchase Return: Qty = 5, Rate = 100 + pr = make_purchase_receipt(company=company, posting_date='2020-04-10', + warehouse="Stores - _TC", item_code=item_code, qty=5, rate=100) + + #Delivery Note: Qty = 5, Rate = 150 + dn = create_delivery_note(item_code=item_code, qty=5, rate=150, warehouse="Stores - _TC", + company=company, expense_account="Cost of Goods Sold - _TC", cost_center="Main - _TC") + + # check outgoing_rate for DN + outgoing_rate = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note", + "voucher_no": dn.name}, "stock_value_difference") / 5) + + self.assertEqual(dn.items[0].incoming_rate, 100) + self.assertEqual(outgoing_rate, 100) + + # Return Entry: Qty = -2, Rate = 150 + return_dn = create_delivery_note(is_return=1, return_against=dn.name, item_code=item_code, qty=-2, rate=150, + company=company, warehouse="Stores - _TC", expense_account="Cost of Goods Sold - _TC", cost_center="Main - _TC") + + # check incoming rate for Return entry + incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", + {"voucher_type": "Delivery Note", "voucher_no": return_dn.name}, + ["incoming_rate", "stock_value_difference"]) + + self.assertEqual(return_dn.items[0].incoming_rate, 100) + self.assertEqual(incoming_rate, 100) + self.assertEqual(stock_value_difference, 200) + + #------------------------------- + + # Landed Cost Voucher to update the rate of incoming Purchase Return: Additional cost = 50 + lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) + + # check outgoing_rate for DN after reposting + outgoing_rate = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note", + "voucher_no": dn.name}, "stock_value_difference") / 5) + self.assertEqual(outgoing_rate, 110) + + dn.reload() + self.assertEqual(dn.items[0].incoming_rate, 110) + + # check incoming rate for Return entry after reposting + incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", + {"voucher_type": "Delivery Note", "voucher_no": return_dn.name}, + ["incoming_rate", "stock_value_difference"]) + + self.assertEqual(incoming_rate, 110) + self.assertEqual(stock_value_difference, 220) + + return_dn.reload() + self.assertEqual(return_dn.items[0].incoming_rate, 110) + + # Cleanup data + return_dn.cancel() + dn.cancel() + lcv.cancel() + pr.cancel() + + def test_reposting_of_sales_return_for_packed_item(self): + company = "_Test Company" + packed_item_code="_Test Item for Reposting" + bundled_item = "_Test Bundled Item for Reposting" + create_product_bundle_item(bundled_item, [[packed_item_code, 4]]) + + # Purchase Return: Qty = 50, Rate = 100 + pr = make_purchase_receipt(company=company, posting_date='2020-04-10', + warehouse="Stores - _TC", item_code=packed_item_code, qty=50, rate=100) + + #Delivery Note: Qty = 5, Rate = 150 + dn = create_delivery_note(item_code=bundled_item, qty=5, rate=150, warehouse="Stores - _TC", + company=company, expense_account="Cost of Goods Sold - _TC", cost_center="Main - _TC") + + # check outgoing_rate for DN + outgoing_rate = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note", + "voucher_no": dn.name}, "stock_value_difference") / 20) + + self.assertEqual(dn.packed_items[0].incoming_rate, 100) + self.assertEqual(outgoing_rate, 100) + + # Return Entry: Qty = -2, Rate = 150 + return_dn = create_delivery_note(is_return=1, return_against=dn.name, item_code=bundled_item, qty=-2, rate=150, + company=company, warehouse="Stores - _TC", expense_account="Cost of Goods Sold - _TC", cost_center="Main - _TC") + + # check incoming rate for Return entry + incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", + {"voucher_type": "Delivery Note", "voucher_no": return_dn.name}, + ["incoming_rate", "stock_value_difference"]) + + self.assertEqual(return_dn.packed_items[0].incoming_rate, 100) + self.assertEqual(incoming_rate, 100) + self.assertEqual(stock_value_difference, 800) + + #------------------------------- + + # Landed Cost Voucher to update the rate of incoming Purchase Return: Additional cost = 50 + lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) + + # check outgoing_rate for DN after reposting + outgoing_rate = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note", + "voucher_no": dn.name}, "stock_value_difference") / 20) + self.assertEqual(outgoing_rate, 101) + + dn.reload() + self.assertEqual(dn.packed_items[0].incoming_rate, 101) + + # check incoming rate for Return entry after reposting + incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", + {"voucher_type": "Delivery Note", "voucher_no": return_dn.name}, + ["incoming_rate", "stock_value_difference"]) + + self.assertEqual(incoming_rate, 101) + self.assertEqual(stock_value_difference, 808) + + return_dn.reload() + self.assertEqual(return_dn.packed_items[0].incoming_rate, 101) + + # Cleanup data + return_dn.cancel() + dn.cancel() + lcv.cancel() + pr.cancel() + + def test_sub_contracted_item_costing(self): + from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom + + company = "_Test Company" + rm_item_code="_Test Item for Reposting" + subcontracted_item = "_Test Subcontracted Item for Reposting" + + frappe.db.set_value("Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM") + make_bom(item = subcontracted_item, raw_materials =[rm_item_code], currency="INR") + + # Purchase raw materials on supplier warehouse: Qty = 50, Rate = 100 + pr = make_purchase_receipt(company=company, posting_date='2020-04-10', + warehouse="Stores - _TC", item_code=rm_item_code, qty=10, rate=100) + + # Purchase Receipt for subcontracted item + pr1 = make_purchase_receipt(company=company, posting_date='2020-04-20', + warehouse="Finished Goods - _TC", supplier_warehouse="Stores - _TC", + item_code=subcontracted_item, qty=10, rate=20, is_subcontracted="Yes") + + self.assertEqual(pr1.items[0].valuation_rate, 120) + + # Update raw material's valuation via LCV, Additional cost = 50 + lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) + + pr1.reload() + self.assertEqual(pr1.items[0].valuation_rate, 125) + + # check outgoing_rate for DN after reposting + incoming_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt", + "voucher_no": pr1.name, "item_code": subcontracted_item}, "incoming_rate") + self.assertEqual(incoming_rate, 125) + + # cleanup data + pr1.cancel() + lcv.cancel() + pr.cancel() + + def test_back_dated_entry_not_allowed(self): + # Back dated stock transactions are only allowed to stock managers + frappe.db.set_value("Stock Settings", None, + "role_allowed_to_create_edit_back_dated_transactions", "Stock Manager") + + # Set User with Stock User role but not Stock Manager + frappe.set_user("test@example.com") + user = frappe.get_doc("User", "test@example.com") + user.add_roles("Stock User") + user.remove_roles("Stock Manager") + + stock_entry_on_today = make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100) + back_dated_se_1 = make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100, + posting_date=add_days(today(), -1), do_not_submit=True) + + # Block back-dated entry + self.assertRaises(BackDatedStockTransaction, back_dated_se_1.submit) + + user.add_roles("Stock Manager") + + # Back dated entry allowed to Stock Manager + back_dated_se_2 = make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100, + posting_date=add_days(today(), -1)) + + back_dated_se_2.cancel() + stock_entry_on_today.cancel() + + frappe.db.set_value("Stock Settings", None, "role_allowed_to_create_edit_back_dated_transactions", None) + frappe.set_user("Administrator") + + +def create_repack_entry(**args): + args = frappe._dict(args) + repack = frappe.new_doc("Stock Entry") + repack.stock_entry_type = "Repack" + repack.company = args.company or "_Test Company" + repack.posting_date = args.posting_date + repack.set_posting_time = 1 + repack.append("items", { + "item_code": "_Test Item for Reposting", + "s_warehouse": "Stores - _TC", + "qty": 5, + "conversion_factor": 1, + "expense_account": "Stock Adjustment - _TC", + "cost_center": "Main - _TC" + }) + + repack.append("items", { + "item_code": "_Test Finished Item for Reposting", + "t_warehouse": "Finished Goods - _TC", + "qty": 1, + "conversion_factor": 1, + "expense_account": "Stock Adjustment - _TC", + "cost_center": "Main - _TC" + }) + + repack.append("additional_costs", { + "expense_account": "Freight and Forwarding Charges - _TC", + "description": "transport cost", + "amount": 40 + }) + + repack.save() + repack.submit() + + return repack + +def create_product_bundle_item(new_item_code, packed_items): + if not frappe.db.exists("Product Bundle", new_item_code): + item = frappe.new_doc("Product Bundle") + item.new_item_code = new_item_code + + for d in packed_items: + item.append("items", { + "item_code": d[0], + "qty": d[1] + }) + + item.save() + +def create_items(): + items = ["_Test Item for Reposting", "_Test Finished Item for Reposting", + "_Test Subcontracted Item for Reposting", "_Test Bundled Item for Reposting"] + for d in items: + properties = {"valuation_method": "FIFO"} + if d == "_Test Bundled Item for Reposting": + properties.update({"is_stock_item": 0}) + elif d == "_Test Subcontracted Item for Reposting": + properties.update({"is_sub_contracted_item": 1}) + + make_item(d, properties=properties) + + return items \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 00b8f69c08..5b40292ea8 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -37,14 +37,16 @@ class StockReconciliation(StockController): def on_submit(self): self.update_stock_ledger() self.make_gl_entries() + self.repost_future_sle_and_gle() from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit update_serial_nos_after_submit(self, "items") def on_cancel(self): - self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') + self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation') self.make_sle_on_cancel() self.make_gl_entries_on_cancel() + self.repost_future_sle_and_gle() def remove_items_with_no_change(self): """Remove items if qty or rate is not changed""" diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 23d48d4ac7..088456f865 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -8,12 +8,11 @@ from __future__ import unicode_literals import frappe, unittest from frappe.utils import flt, nowdate, nowtime from erpnext.accounts.utils import get_stock_and_account_balance -from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import EmptyStockReconciliationItemsError, get_items from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse from erpnext.stock.doctype.item.test_item import create_item -from erpnext.stock.utils import get_stock_balance, get_incoming_rate, get_available_serial_nos, get_stock_value_on +from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos class TestStockReconciliation(unittest.TestCase): @@ -29,16 +28,17 @@ class TestStockReconciliation(unittest.TestCase): self._test_reco_sle_gle("Moving Average") def _test_reco_sle_gle(self, valuation_method): - insert_existing_sle(warehouse='Stores - TCP1') + se1, se2, se3 = insert_existing_sle(warehouse='Stores - TCP1') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company') # [[qty, valuation_rate, posting_date, # posting_time, expected_stock_value, bin_qty, bin_valuation]] + input_data = [ - [50, 1000], - [25, 900], - ["", 1000], - [20, ""], - [0, ""] + [50, 1000, "2012-12-26", "12:00"], + [25, 900, "2012-12-26", "12:00"], + ["", 1000, "2012-12-20", "12:05"], + [20, "", "2012-12-26", "12:05"], + [0, "", "2012-12-31", "12:10"] ] for d in input_data: @@ -47,13 +47,13 @@ class TestStockReconciliation(unittest.TestCase): last_sle = get_previous_sle({ "item_code": "_Test Item", "warehouse": "Stores - TCP1", - "posting_date": nowdate(), - "posting_time": nowtime() + "posting_date": d[2], + "posting_time": d[3] }) # submit stock reconciliation stock_reco = create_stock_reconciliation(qty=d[0], rate=d[1], - posting_date=nowdate(), posting_time=nowtime(), warehouse="Stores - TCP1", + posting_date=d[2], posting_time=d[3], warehouse="Stores - TCP1", company=company, expense_account = "Stock Adjustment - TCP1") # check stock value @@ -81,10 +81,15 @@ class TestStockReconciliation(unittest.TestCase): stock_reco.cancel() + se3.cancel() + se2.cancel() + se1.cancel() + def test_get_items(self): - create_warehouse("_Test Warehouse Group 1", {"is_group": 1}) + create_warehouse("_Test Warehouse Group 1", + {"is_group": 1, "company": "_Test Company", "parent_warehouse": "All Warehouses - _TC"}) create_warehouse("_Test Warehouse Ledger 1", - {"is_group": 0, "parent_warehouse": "_Test Warehouse Group 1 - _TC"}) + {"is_group": 0, "parent_warehouse": "_Test Warehouse Group 1 - _TC", "company": "_Test Company"}) create_item("_Test Stock Reco Item", is_stock_item=1, valuation_rate=100, warehouse="_Test Warehouse Ledger 1 - _TC", opening_stock=100) @@ -95,8 +100,6 @@ class TestStockReconciliation(unittest.TestCase): [items[0]["item_code"], items[0]["warehouse"], items[0]["qty"]]) def test_stock_reco_for_serialized_item(self): - set_perpetual_inventory() - to_delete_records = [] to_delete_serial_nos = [] @@ -148,8 +151,6 @@ class TestStockReconciliation(unittest.TestCase): stock_doc.cancel() def test_stock_reco_for_batch_item(self): - set_perpetual_inventory() - to_delete_records = [] to_delete_serial_nos = [] @@ -196,15 +197,17 @@ class TestStockReconciliation(unittest.TestCase): def insert_existing_sle(warehouse): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry - make_stock_entry(posting_date=nowdate(), posting_time=nowtime(), item_code="_Test Item", + se1 = make_stock_entry(posting_date="2012-12-15", posting_time="02:00", item_code="_Test Item", target=warehouse, qty=10, basic_rate=700) - make_stock_entry(posting_date=nowdate(), posting_time=nowtime(), item_code="_Test Item", + se2 = make_stock_entry(posting_date="2012-12-25", posting_time="03:00", item_code="_Test Item", source=warehouse, qty=15) - make_stock_entry(posting_date=nowdate(), posting_time=nowtime(), item_code="_Test Item", + se3 = make_stock_entry(posting_date="2013-01-05", posting_time="07:00", item_code="_Test Item", target=warehouse, qty=15, basic_rate=1200) + return se1, se2, se3 + def create_batch_or_serial_no_items(): create_warehouse("_Test Warehouse for Stock Reco1", {"is_group": 0, "parent_warehouse": "_Test Warehouse Group - _TC"}) @@ -256,6 +259,10 @@ def create_stock_reconciliation(**args): return sr def set_valuation_method(item_code, valuation_method): + existing_valuation_method = get_valuation_method(item_code) + if valuation_method == existing_valuation_method: + return + frappe.db.set_value("Item", item_code, "valuation_method", valuation_method) for warehouse in frappe.get_all("Warehouse", filters={"company": "_Test Company"}, fields=["name", "is_group"]): diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index a1666579d1..859aea2eb6 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -28,7 +28,9 @@ "inter_warehouse_transfer_settings_section", "allow_from_dn", "allow_from_pr", - "freeze_stock_entries", + "control_historical_stock_transactions_section", + "role_allowed_to_create_edit_back_dated_transactions", + "column_break_26", "stock_frozen_upto", "stock_frozen_upto_days", "stock_auth_role", @@ -156,21 +158,20 @@ "label": "Notify by Email on Creation of Automatic Material Request" }, { - "fieldname": "freeze_stock_entries", - "fieldtype": "Section Break", - "label": "Freeze Stock Entries" - }, - { + "description": "No stock transactions can be created or modified before this date.", "fieldname": "stock_frozen_upto", "fieldtype": "Date", "label": "Stock Frozen Upto" }, { + "description": "Stock transactions that are older than the mentioned days cannot be modified.", "fieldname": "stock_frozen_upto_days", "fieldtype": "Int", "label": "Freeze Stocks Older Than (Days)" }, { + "depends_on": "eval:(doc.stock_frozen_upto || doc.stock_frozen_upto_days)", + "description": "The users with this Role are allowed to create/modify a stock transaction, even though the transaction is frozen.", "fieldname": "stock_auth_role", "fieldtype": "Link", "label": "Role Allowed to Edit Frozen Stock", @@ -210,6 +211,22 @@ "fieldname": "allow_from_pr", "fieldtype": "Check", "label": "Allow Material Transfer from Purchase Receipt to Purchase Invoice" + }, + { + "description": "If mentioned, the system will allow only the users with this Role to create or modify any stock transaction earlier than the latest stock transaction for a specific item and warehouse. If set as blank, it allows all users to create/edit back-dated transactions.", + "fieldname": "role_allowed_to_create_edit_back_dated_transactions", + "fieldtype": "Link", + "label": "Role Allowed to Create/Edit Back-dated Transactions", + "options": "User" + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "fieldname": "control_historical_stock_transactions_section", + "fieldtype": "Section Break", + "label": "Control Historical Stock Transactions" } ], "icon": "icon-cog", @@ -217,7 +234,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2020-11-23 15:26:54.225608", + "modified": "2020-11-23 22:26:54.225608", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.py b/erpnext/stock/doctype/warehouse/test_warehouse.py index 3101e8af4c..95478f61f0 100644 --- a/erpnext/stock/doctype/warehouse/test_warehouse.py +++ b/erpnext/stock/doctype/warehouse/test_warehouse.py @@ -10,13 +10,10 @@ from frappe.test_runner import make_test_records import erpnext from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry -from erpnext import set_perpetual_inventory from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account - test_records = frappe.get_test_records('Warehouse') - class TestWarehouse(unittest.TestCase): def setUp(self): if not frappe.get_value('Item', '_Test Item'): @@ -37,63 +34,63 @@ class TestWarehouse(unittest.TestCase): self.assertEqual(child_warehouse.is_group, 0) def test_warehouse_renaming(self): - set_perpetual_inventory(1) - create_warehouse("Test Warehouse for Renaming 1") - account = get_inventory_account("_Test Company", "Test Warehouse for Renaming 1 - _TC") + create_warehouse("Test Warehouse for Renaming 1", company="_Test Company with perpetual inventory") + account = get_inventory_account("_Test Company with perpetual inventory", "Test Warehouse for Renaming 1 - TCP1") self.assertTrue(frappe.db.get_value("Warehouse", filters={"account": account})) # Rename with abbr - if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 2 - _TC"): - frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC") - frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 1 - _TC", "Test Warehouse for Renaming 2 - _TC") + if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 2 - TCP1"): + frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 2 - TCP1") + frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 1 - TCP1", "Test Warehouse for Renaming 2 - TCP1") self.assertTrue(frappe.db.get_value("Warehouse", - filters={"account": "Test Warehouse for Renaming 1 - _TC"})) + filters={"account": "Test Warehouse for Renaming 1 - TCP1"})) # Rename without abbr - if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 3 - _TC"): - frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC") + if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 3 - TCP1"): + frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 3 - TCP1") - frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC", "Test Warehouse for Renaming 3") + frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 2 - TCP1", "Test Warehouse for Renaming 3") self.assertTrue(frappe.db.get_value("Warehouse", - filters={"account": "Test Warehouse for Renaming 1 - _TC"})) + filters={"account": "Test Warehouse for Renaming 1 - TCP1"})) # Another rename with multiple dashes - if frappe.db.exists("Warehouse", "Test - Warehouse - Company - _TC"): - frappe.delete_doc("Warehouse", "Test - Warehouse - Company - _TC") - frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC", "Test - Warehouse - Company") + if frappe.db.exists("Warehouse", "Test - Warehouse - Company - TCP1"): + frappe.delete_doc("Warehouse", "Test - Warehouse - Company - TCP1") + frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 3 - TCP1", "Test - Warehouse - Company") def test_warehouse_merging(self): - set_perpetual_inventory(1) + company = "_Test Company with perpetual inventory" + create_warehouse("Test Warehouse for Merging 1", company=company, + properties={"parent_warehouse": "All Warehouses - TCP1"}) + create_warehouse("Test Warehouse for Merging 2", company=company, + properties={"parent_warehouse": "All Warehouses - TCP1"}) - create_warehouse("Test Warehouse for Merging 1") - create_warehouse("Test Warehouse for Merging 2") - - make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 1 - _TC", - qty=1, rate=100) - make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 2 - _TC", - qty=1, rate=100) + make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 1 - TCP1", + qty=1, rate=100, company=company) + make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 2 - TCP1", + qty=1, rate=100, company=company) existing_bin_qty = ( cint(frappe.db.get_value("Bin", - {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 1 - _TC"}, "actual_qty")) + {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 1 - TCP1"}, "actual_qty")) + cint(frappe.db.get_value("Bin", - {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty")) + {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - TCP1"}, "actual_qty")) ) - frappe.rename_doc("Warehouse", "Test Warehouse for Merging 1 - _TC", - "Test Warehouse for Merging 2 - _TC", merge=True) + frappe.rename_doc("Warehouse", "Test Warehouse for Merging 1 - TCP1", + "Test Warehouse for Merging 2 - TCP1", merge=True) - self.assertFalse(frappe.db.exists("Warehouse", "Test Warehouse for Merging 1 - _TC")) + self.assertFalse(frappe.db.exists("Warehouse", "Test Warehouse for Merging 1 - TCP1")) bin_qty = frappe.db.get_value("Bin", - {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty") + {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - TCP1"}, "actual_qty") self.assertEqual(bin_qty, existing_bin_qty) self.assertTrue(frappe.db.get_value("Warehouse", - filters={"account": "Test Warehouse for Merging 2 - _TC"})) + filters={"account": "Test Warehouse for Merging 2 - TCP1"})) def create_warehouse(warehouse_name, properties=None, company=None): if not company: diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index cd86be3115..6c84f168fd 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -29,7 +29,6 @@ class Warehouse(NestedSet): self.set_onload('account', account) load_address_and_contact(self) - def on_update(self): self.update_nsm_model() diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py index 54eefdfaaa..0cc8ca48aa 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.py +++ b/erpnext/stock/report/stock_analytics/stock_analytics.py @@ -7,9 +7,11 @@ from frappe import _, scrub from frappe.utils import getdate, flt from erpnext.stock.report.stock_balance.stock_balance import (get_items, get_stock_ledger_entries, get_item_details) from erpnext.accounts.utils import get_fiscal_year +from erpnext.stock.utils import is_reposting_item_valuation_in_progress from six import iteritems def execute(filters=None): + is_reposting_item_valuation_in_progress() filters = frappe._dict(filters or {}) columns = get_columns(filters) data = get_data(filters) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index ccd01001bb..e5d4d626c4 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -7,12 +7,13 @@ from frappe import _ from frappe.utils import flt, cint, getdate, now, date_diff from erpnext.stock.utils import add_additional_uom_columns from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition - +from erpnext.stock.utils import is_reposting_item_valuation_in_progress from erpnext.stock.report.stock_ageing.stock_ageing import get_fifo_queue, get_average_age from six import iteritems def execute(filters=None): + is_reposting_item_valuation_in_progress() if not filters: filters = {} validate_filters(filters) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index 86af5e0c86..7b5701a993 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -5,11 +5,12 @@ from __future__ import unicode_literals import frappe from frappe.utils import cint, flt -from erpnext.stock.utils import update_included_uom_in_report +from erpnext.stock.utils import update_included_uom_in_report, is_reposting_item_valuation_in_progress from frappe import _ from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos def execute(filters=None): + is_reposting_item_valuation_in_progress() include_uom = filters.get("include_uom") columns = get_columns() items = get_items(filters) diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py index c8efb1637f..1183e41d04 100644 --- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py +++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py @@ -5,9 +5,10 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import flt, today -from erpnext.stock.utils import update_included_uom_in_report +from erpnext.stock.utils import update_included_uom_in_report, is_reposting_item_valuation_in_progress def execute(filters=None): + is_reposting_item_valuation_in_progress() filters = frappe._dict(filters or {}) include_uom = filters.get("include_uom") columns = get_columns() diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py index ebcb106b02..04f7d347ba 100644 --- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py +++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py @@ -11,9 +11,11 @@ from frappe.utils import flt, cint, getdate from erpnext.stock.report.stock_balance.stock_balance import (get_item_details, get_item_reorder_details, get_item_warehouse_map, get_items, get_stock_ledger_entries) from erpnext.stock.report.stock_ageing.stock_ageing import get_fifo_queue, get_average_age +from erpnext.stock.utils import is_reposting_item_valuation_in_progress from six import iteritems def execute(filters=None): + is_reposting_item_valuation_in_progress() if not filters: filters = {} validate_filters(filters) diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py index b5ae1b78eb..8ba1f1ca5c 100644 --- a/erpnext/stock/stock_balance.py +++ b/erpnext/stock/stock_balance.py @@ -6,6 +6,7 @@ import frappe from frappe.utils import flt, cstr, nowdate, nowtime from erpnext.stock.utils import update_bin from erpnext.stock.stock_ledger import update_entries_after +from erpnext.controllers.stock_controller import create_repost_item_valuation_entry def repost(only_actual=False, allow_negative_stock=False, allow_zero_rate=False, only_bin=False): """ @@ -56,12 +57,18 @@ def repost_stock(item_code, warehouse, allow_zero_rate=False, update_bin_qty(item_code, warehouse, qty_dict) def repost_actual_qty(item_code, warehouse, allow_zero_rate=False, allow_negative_stock=False): - update_entries_after({ "item_code": item_code, "warehouse": warehouse }, - allow_zero_rate=allow_zero_rate, allow_negative_stock=allow_negative_stock) + create_repost_item_valuation_entry({ + "item_code": item_code, + "warehouse": warehouse, + "posting_date": "1900-01-01", + "posting_time": "00:01", + "allow_negative_stock": allow_negative_stock, + "allow_zero_rate": allow_zero_rate + }) def get_balance_qty_from_sle(item_code, warehouse): balance_qty = frappe.db.sql("""select qty_after_transaction from `tabStock Ledger Entry` - where item_code=%s and warehouse=%s + where item_code=%s and warehouse=%s and is_cancelled=0 order by posting_date desc, posting_time desc, creation desc limit 1""", (item_code, warehouse)) @@ -191,7 +198,7 @@ def set_stock_balance_as_per_serial_no(item_code=None, posting_date=None, postin print(d[0], d[1], d[2], serial_nos[0][0]) sle = frappe.db.sql("""select valuation_rate, company from `tabStock Ledger Entry` - where item_code = %s and warehouse = %s + where item_code = %s and warehouse = %s and is_cancelled = 0 order by posting_date desc limit 1""", (d[0], d[1])) sle_dict = { @@ -223,7 +230,8 @@ def set_stock_balance_as_per_serial_no(item_code=None, posting_date=None, postin }) update_bin(args) - update_entries_after({ + + create_repost_item_valuation_entry({ "item_code": d[0], "warehouse": d[1], "posting_date": posting_date, diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index f4490f1b01..5b9ada0ee5 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -5,9 +5,10 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _ from frappe.utils import cint, flt, cstr, now, now_datetime +from frappe.model.meta import get_field_precision from erpnext.stock.utils import get_valuation_method, get_incoming_outgoing_rate_for_cancel +from erpnext.stock.utils import get_bin import json - from six import iteritems # future reposting @@ -25,32 +26,23 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no')) for sle in sl_entries: - sle_id = None - if via_landed_cost_voucher or cancel: - sle['posting_date'] = now_datetime().strftime('%Y-%m-%d') - sle['posting_time'] = now_datetime().strftime('%H:%M:%S.%f') + if cancel: + sle['actual_qty'] = -flt(sle.get('actual_qty')) - if cancel: - sle['actual_qty'] = -flt(sle.get('actual_qty')) - - if sle['actual_qty'] < 0 and not sle.get('outgoing_rate'): - sle['outgoing_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code, - sle.voucher_type, sle.voucher_no, sle.voucher_detail_no) - sle['incoming_rate'] = 0.0 - - if sle['actual_qty'] > 0 and not sle.get('incoming_rate'): - sle['incoming_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code, - sle.voucher_type, sle.voucher_no, sle.voucher_detail_no) - sle['outgoing_rate'] = 0.0 + if sle['actual_qty'] < 0 and not sle.get('outgoing_rate'): + sle['outgoing_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code, + sle.voucher_type, sle.voucher_no, sle.voucher_detail_no) + sle['incoming_rate'] = 0.0 + if sle['actual_qty'] > 0 and not sle.get('incoming_rate'): + sle['incoming_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code, + sle.voucher_type, sle.voucher_no, sle.voucher_detail_no) + sle['outgoing_rate'] = 0.0 if sle.get("actual_qty") or sle.get("voucher_type")=="Stock Reconciliation": - sle_id = make_entry(sle, allow_negative_stock, via_landed_cost_voucher) - - args = sle.copy() - args.update({ - "sle_id": sle_id - }) + sle_doc = make_entry(sle, allow_negative_stock, via_landed_cost_voucher) + + args = sle_doc.as_dict() update_bin(args, allow_negative_stock, via_landed_cost_voucher) @@ -68,8 +60,36 @@ def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False): sle.via_landed_cost_voucher = via_landed_cost_voucher sle.insert() sle.submit() - return sle.name + return sle +def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=False, via_landed_cost_voucher=False): + if not args and voucher_type and voucher_no: + args = get_args_for_voucher(voucher_type, voucher_no) + + distinct_item_warehouses = [(d.item_code, d.warehouse) for d in args] + + i = 0 + while i < len(args): + obj = update_entries_after({ + "item_code": args[i].item_code, + "warehouse": args[i].warehouse, + "posting_date": args[i].posting_date, + "posting_time": args[i].posting_time + }, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher) + + for item_wh, new_sle in iteritems(obj.new_items): + if item_wh not in distinct_item_warehouses: + args.append(new_sle) + + i += 1 + +def get_args_for_voucher(voucher_type, voucher_no): + return frappe.db.get_all("Stock Ledger Entry", + filters={"voucher_type": voucher_type, "voucher_no": voucher_no}, + fields=["item_code", "warehouse", "posting_date", "posting_time"], + order_by="creation asc", + group_by="item_code, warehouse" + ) class update_entries_after(object): """ @@ -86,141 +106,299 @@ class update_entries_after(object): } """ def __init__(self, args, allow_zero_rate=False, allow_negative_stock=None, via_landed_cost_voucher=False, verbose=1): - from frappe.model.meta import get_field_precision - - self.exceptions = [] + self.exceptions = {} self.verbose = verbose self.allow_zero_rate = allow_zero_rate - self.allow_negative_stock = allow_negative_stock self.via_landed_cost_voucher = via_landed_cost_voucher - if not self.allow_negative_stock: - self.allow_negative_stock = cint(frappe.db.get_single_value("Stock Settings", - "allow_negative_stock")) + self.allow_negative_stock = allow_negative_stock \ + or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) - self.args = args - for key, value in iteritems(args): - setattr(self, key, value) + self.args = frappe._dict(args) + self.item_code = args.get("item_code") + if self.args.sle_id: + self.args['name'] = self.args.sle_id - self.previous_sle = self.get_sle_before_datetime() - self.previous_sle = self.previous_sle[0] if self.previous_sle else frappe._dict() + self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company") + self.get_precision() + self.valuation_method = get_valuation_method(self.item_code) + self.new_items = {} + + self.data = frappe._dict() + self.initialize_previous_data(self.args) + + self.build() + + def get_precision(self): + company_base_currency = frappe.get_cached_value('Company', self.company, "default_currency") + self.precision = get_field_precision(frappe.get_meta("Stock Ledger Entry").get_field("stock_value"), + currency=company_base_currency) + + def initialize_previous_data(self, args): + """ + Get previous sl entries for current item for each related warehouse + and assigns into self.data dict + + :Data Structure: + + self.data = { + warehouse1: { + 'previus_sle': {}, + 'qty_after_transaction': 10, + 'valuation_rate': 100, + 'stock_value': 1000, + 'prev_stock_value': 1000, + 'stock_queue': '[[10, 100]]', + 'stock_value_difference': 1000 + } + } + + """ + self.data.setdefault(args.warehouse, frappe._dict()) + warehouse_dict = self.data[args.warehouse] + previous_sle = self.get_sle_before_datetime(args) + warehouse_dict.previous_sle = previous_sle for key in ("qty_after_transaction", "valuation_rate", "stock_value"): - setattr(self, key, flt(self.previous_sle.get(key))) + setattr(warehouse_dict, key, flt(previous_sle.get(key))) - self.company = frappe.db.get_value("Warehouse", self.warehouse, "company") - self.precision = get_field_precision(frappe.get_meta("Stock Ledger Entry").get_field("stock_value"), - currency=frappe.get_cached_value('Company', self.company, "default_currency")) + warehouse_dict.update({ + "prev_stock_value": previous_sle.stock_value or 0.0, + "stock_queue": json.loads(previous_sle.stock_queue or "[]"), + "stock_value_difference": 0.0 + }) - self.prev_stock_value = self.previous_sle.stock_value or 0.0 - self.stock_queue = json.loads(self.previous_sle.stock_queue or "[]") - self.valuation_method = get_valuation_method(self.item_code) - self.stock_value_difference = 0.0 - self.build(args.get('sle_id')) - - def build(self, sle_id): - if sle_id: - sle = get_sle_by_id(sle_id) - self.process_sle(sle) + def build(self): + if self.args.get("sle_id"): + self.process_sle_against_current_voucher() else: - # includes current entry! - entries_to_fix = self.get_sle_after_datetime() - for sle in entries_to_fix: + entries_to_fix = self.get_future_entries_to_fix() + + i = 0 + while i < len(entries_to_fix): + sle = entries_to_fix[i] + i += 1 + self.process_sle(sle) + if sle.dependant_sle_voucher_detail_no: + self.get_dependent_entries_to_fix(entries_to_fix, sle) + if self.exceptions: self.raise_exceptions() self.update_bin() - def update_bin(self): - # update bin - bin_name = frappe.db.get_value("Bin", { - "item_code": self.item_code, - "warehouse": self.warehouse - }) + def process_sle_against_current_voucher(self): + sl_entries = self.get_sle_against_current_voucher() + for sle in sl_entries: + self.process_sle(sle) - if not bin_name: - bin_doc = frappe.get_doc({ - "doctype": "Bin", - "item_code": self.item_code, - "warehouse": self.warehouse - }) - bin_doc.insert(ignore_permissions=True) - else: - bin_doc = frappe.get_doc("Bin", bin_name) + def get_sle_against_current_voucher(self): + return frappe.db.sql(""" + select + *, timestamp(posting_date, posting_time) as "timestamp" + from + `tabStock Ledger Entry` + where + item_code = %(item_code)s + and warehouse = %(warehouse)s + and voucher_type = %(voucher_type)s + and voucher_no = %(voucher_no)s + order by + creation ASC + for update + """, self.args, as_dict=1) - bin_doc.update({ - "valuation_rate": self.valuation_rate, - "actual_qty": self.qty_after_transaction, - "stock_value": self.stock_value - }) - bin_doc.flags.via_stock_ledger_entry = True + def get_future_entries_to_fix(self): + # includes current entry! + args = self.data[self.args.warehouse].previous_sle \ + or frappe._dict({"item_code": self.item_code, "warehouse": self.args.warehouse}) + + return list(self.get_sle_after_datetime(args)) - bin_doc.save(ignore_permissions=True) + def get_dependent_entries_to_fix(self, entries_to_fix, sle): + dependant_sle = get_sle_by_voucher_detail_no(sle.dependant_sle_voucher_detail_no, + excluded_sle=sle.name) + + if not dependant_sle: + return + elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse: + return + elif dependant_sle.item_code != self.item_code \ + and (dependant_sle.item_code, dependant_sle.warehouse) not in self.new_items: + self.new_items[(dependant_sle.item_code, dependant_sle.warehouse)] = dependant_sle + return + + self.initialize_previous_data(dependant_sle) + + args = self.data[dependant_sle.warehouse].previous_sle \ + or frappe._dict({"item_code": self.item_code, "warehouse": dependant_sle.warehouse}) + future_sle_for_dependant = list(self.get_sle_after_datetime(args)) + + entries_to_fix.extend(future_sle_for_dependant) + entries_to_fix = sorted(entries_to_fix, key=lambda k: k['timestamp']) def process_sle(self, sle): + # previous sle data for this warehouse + self.wh_data = self.data[sle.warehouse] + if (sle.serial_no and not self.via_landed_cost_voucher) or not cint(self.allow_negative_stock): # validate negative stock for serialized items, fifo valuation # or when negative stock is not allowed for moving average if not self.validate_negative_stock(sle): - self.qty_after_transaction += flt(sle.actual_qty) + self.wh_data.qty_after_transaction += flt(sle.actual_qty) return + # Get dynamic incoming/outgoing rate + self.get_dynamic_incoming_outgoing_rate(sle) + if sle.serial_no: self.get_serialized_values(sle) - self.qty_after_transaction += flt(sle.actual_qty) + self.wh_data.qty_after_transaction += flt(sle.actual_qty) if sle.voucher_type == "Stock Reconciliation": - self.qty_after_transaction = sle.qty_after_transaction + self.wh_data.qty_after_transaction = sle.qty_after_transaction - self.stock_value = flt(self.qty_after_transaction) * flt(self.valuation_rate) + self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(self.wh_data.valuation_rate) else: if sle.voucher_type=="Stock Reconciliation" and not sle.batch_no: # assert - self.valuation_rate = sle.valuation_rate - self.qty_after_transaction = sle.qty_after_transaction - self.stock_queue = [[self.qty_after_transaction, self.valuation_rate]] - self.stock_value = flt(self.qty_after_transaction) * flt(self.valuation_rate) + self.wh_data.valuation_rate = sle.valuation_rate + self.wh_data.qty_after_transaction = sle.qty_after_transaction + self.wh_data.stock_queue = [[self.wh_data.qty_after_transaction, self.wh_data.valuation_rate]] + self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(self.wh_data.valuation_rate) else: if self.valuation_method == "Moving Average": self.get_moving_average_values(sle) - self.qty_after_transaction += flt(sle.actual_qty) - self.stock_value = flt(self.qty_after_transaction) * flt(self.valuation_rate) + self.wh_data.qty_after_transaction += flt(sle.actual_qty) + self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(self.wh_data.valuation_rate) else: self.get_fifo_values(sle) - self.qty_after_transaction += flt(sle.actual_qty) - self.stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in self.stock_queue)) + self.wh_data.qty_after_transaction += flt(sle.actual_qty) + self.wh_data.stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in self.wh_data.stock_queue)) # rounding as per precision - self.stock_value = flt(self.stock_value, self.precision) - - stock_value_difference = self.stock_value - self.prev_stock_value - - self.prev_stock_value = self.stock_value + self.wh_data.stock_value = flt(self.wh_data.stock_value, self.precision) + stock_value_difference = self.wh_data.stock_value - self.wh_data.prev_stock_value + self.wh_data.prev_stock_value = self.wh_data.stock_value # update current sle - sle.qty_after_transaction = self.qty_after_transaction - sle.valuation_rate = self.valuation_rate - sle.stock_value = self.stock_value - sle.stock_queue = json.dumps(self.stock_queue) + sle.qty_after_transaction = self.wh_data.qty_after_transaction + sle.valuation_rate = self.wh_data.valuation_rate + sle.stock_value = self.wh_data.stock_value + sle.stock_queue = json.dumps(self.wh_data.stock_queue) sle.stock_value_difference = stock_value_difference sle.doctype="Stock Ledger Entry" frappe.get_doc(sle).db_update() + self.update_outgoing_rate_on_transaction(sle) + def validate_negative_stock(self, sle): """ validate negative stock for entries current datetime onwards will not consider cancelled entries """ - diff = self.qty_after_transaction + flt(sle.actual_qty) + diff = self.wh_data.qty_after_transaction + flt(sle.actual_qty) if diff < 0 and abs(diff) > 0.0001: # negative stock! exc = sle.copy().update({"diff": diff}) - self.exceptions.append(exc) + self.exceptions.setdefault(sle.warehouse, []).append(exc) return False else: return True + def get_dynamic_incoming_outgoing_rate(self, sle): + # Get updated incoming/outgoing rate from transaction + if sle.recalculate_rate: + rate = self.get_incoming_outgoing_rate_from_transaction(sle) + + if flt(sle.actual_qty) >= 0: + sle.incoming_rate = rate + else: + sle.outgoing_rate = rate + + def get_incoming_outgoing_rate_from_transaction(self, sle): + rate = 0 + # Material Transfer, Repack, Manufacturing + if sle.voucher_type == "Stock Entry": + rate = frappe.db.get_value("Stock Entry Detail", sle.voucher_detail_no, "valuation_rate") + # Sales and Purchase Return + elif sle.voucher_type in ("Purchase Receipt", "Purchase Invoice", "Delivery Note", "Sales Invoice"): + if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_return"): + from erpnext.controllers.sales_and_purchase_return import get_rate_for_return # don't move this import to top + rate = get_rate_for_return(sle.voucher_type, sle.voucher_no, sle.item_code, voucher_detail_no=sle.voucher_detail_no) + else: + if sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"): + rate_field = "valuation_rate" + else: + rate_field = "incoming_rate" + + # check in item table + item_code, incoming_rate = frappe.db.get_value(sle.voucher_type + " Item", + sle.voucher_detail_no, ["item_code", rate_field]) + + if item_code == sle.item_code: + rate = incoming_rate + else: + if sle.voucher_type in ("Delivery Note", "Sales Invoice"): + ref_doctype = "Packed Item" + else: + ref_doctype = "Purchase Receipt Item Supplied" + + rate = frappe.db.get_value(ref_doctype, {"parent_detail_docname": sle.voucher_detail_no, + "item_code": sle.item_code}, rate_field) + + return rate + + def update_outgoing_rate_on_transaction(self, sle): + """ + Update outgoing rate in Stock Entry, Delivery Note, Sales Invoice and Sales Return + In case of Stock Entry, also calculate FG Item rate and total incoming/outgoing amount + """ + if sle.actual_qty and sle.voucher_detail_no: + outgoing_rate = abs(flt(sle.stock_value_difference)) / abs(sle.actual_qty) + + if flt(sle.actual_qty) < 0 and sle.voucher_type == "Stock Entry": + self.update_rate_on_stock_entry(sle, outgoing_rate) + elif sle.voucher_type in ("Delivery Note", "Sales Invoice"): + self.update_rate_on_delivery_and_sales_return(sle, outgoing_rate) + elif flt(sle.actual_qty) < 0 and sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"): + self.update_rate_on_purchase_receipt(sle, outgoing_rate) + + def update_rate_on_stock_entry(self, sle, outgoing_rate): + frappe.db.set_value("Stock Entry Detail", sle.voucher_detail_no, "basic_rate", outgoing_rate) + + # Update outgoing item's rate, recalculate FG Item's rate and total incoming/outgoing amount + stock_entry = frappe.get_doc("Stock Entry", sle.voucher_no) + stock_entry.calculate_rate_and_amount(reset_outgoing_rate=False, raise_error_if_no_rate=False) + stock_entry.db_update() + for d in stock_entry.items: + d.db_update() + + def update_rate_on_delivery_and_sales_return(self, sle, outgoing_rate): + # Update item's incoming rate on transaction + item_code = frappe.db.get_value(sle.voucher_type + " Item", sle.voucher_detail_no, "item_code") + if item_code == sle.item_code: + frappe.db.set_value(sle.voucher_type + " Item", sle.voucher_detail_no, "incoming_rate", outgoing_rate) + else: + # packed item + frappe.db.set_value("Packed Item", + {"parent_detail_docname": sle.voucher_detail_no, "item_code": sle.item_code}, + "incoming_rate", outgoing_rate) + + def update_rate_on_purchase_receipt(self, sle, outgoing_rate): + if frappe.db.exists(sle.voucher_type + " Item", sle.voucher_detail_no): + frappe.db.set_value(sle.voucher_type + " Item", sle.voucher_detail_no, "base_net_rate", outgoing_rate) + else: + frappe.db.set_value("Purchase Receipt Item Supplied", sle.voucher_detail_no, "rate", outgoing_rate) + + # Recalculate subcontracted item's rate in case of subcontracted purchase receipt/invoice + if frappe.db.get_value(sle.voucher_type, sle.voucher_no, "is_subcontracted"): + doc = frappe.get_cached_doc(sle.voucher_type, sle.voucher_no) + doc.update_valuation_rate(reset_outgoing_rate=False) + for d in (doc.items + doc.supplied_items): + d.db_update() + def get_serialized_values(self, sle): incoming_rate = flt(sle.incoming_rate) actual_qty = flt(sle.actual_qty) @@ -228,7 +406,7 @@ class update_entries_after(object): if incoming_rate < 0: # wrong incoming rate - incoming_rate = self.valuation_rate + incoming_rate = self.wh_data.valuation_rate stock_value_change = 0 if incoming_rate: @@ -236,22 +414,25 @@ class update_entries_after(object): elif actual_qty < 0: # In case of delivery/stock issue, get average purchase rate # of serial nos of current entry - outgoing_value = self.get_incoming_value_for_serial_nos(sle, serial_nos) - stock_value_change = -1 * outgoing_value + if not sle.is_cancelled: + outgoing_value = self.get_incoming_value_for_serial_nos(sle, serial_nos) + stock_value_change = -1 * outgoing_value + else: + stock_value_change = actual_qty * sle.outgoing_rate - new_stock_qty = self.qty_after_transaction + actual_qty + new_stock_qty = self.wh_data.qty_after_transaction + actual_qty if new_stock_qty > 0: - new_stock_value = (self.qty_after_transaction * self.valuation_rate) + stock_value_change + new_stock_value = (self.wh_data.qty_after_transaction * self.wh_data.valuation_rate) + stock_value_change if new_stock_value >= 0: # calculate new valuation rate only if stock value is positive # else it remains the same as that of previous entry - self.valuation_rate = new_stock_value / new_stock_qty + self.wh_data.valuation_rate = new_stock_value / new_stock_qty - if not self.valuation_rate and sle.voucher_detail_no: + if not self.wh_data.valuation_rate and sle.voucher_detail_no: allow_zero_rate = self.check_if_allow_zero_valuation_rate(sle.voucher_type, sle.voucher_detail_no) if not allow_zero_rate: - self.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, + self.wh_data.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, sle.voucher_type, sle.voucher_no, self.allow_zero_rate, currency=erpnext.get_company_currency(sle.company)) @@ -287,39 +468,39 @@ class update_entries_after(object): def get_moving_average_values(self, sle): actual_qty = flt(sle.actual_qty) - new_stock_qty = flt(self.qty_after_transaction) + actual_qty + new_stock_qty = flt(self.wh_data.qty_after_transaction) + actual_qty if new_stock_qty >= 0: if actual_qty > 0: - if flt(self.qty_after_transaction) <= 0: - self.valuation_rate = sle.incoming_rate + if flt(self.wh_data.qty_after_transaction) <= 0: + self.wh_data.valuation_rate = sle.incoming_rate else: - new_stock_value = (self.qty_after_transaction * self.valuation_rate) + \ + new_stock_value = (self.wh_data.qty_after_transaction * self.wh_data.valuation_rate) + \ (actual_qty * sle.incoming_rate) - self.valuation_rate = new_stock_value / new_stock_qty + self.wh_data.valuation_rate = new_stock_value / new_stock_qty elif sle.outgoing_rate: if new_stock_qty: - new_stock_value = (self.qty_after_transaction * self.valuation_rate) + \ + new_stock_value = (self.wh_data.qty_after_transaction * self.wh_data.valuation_rate) + \ (actual_qty * sle.outgoing_rate) - self.valuation_rate = new_stock_value / new_stock_qty + self.wh_data.valuation_rate = new_stock_value / new_stock_qty else: - self.valuation_rate = sle.outgoing_rate + self.wh_data.valuation_rate = sle.outgoing_rate else: - if flt(self.qty_after_transaction) >= 0 and sle.outgoing_rate: - self.valuation_rate = sle.outgoing_rate + if flt(self.wh_data.qty_after_transaction) >= 0 and sle.outgoing_rate: + self.wh_data.valuation_rate = sle.outgoing_rate - if not self.valuation_rate and actual_qty > 0: - self.valuation_rate = sle.incoming_rate + if not self.wh_data.valuation_rate and actual_qty > 0: + self.wh_data.valuation_rate = sle.incoming_rate # Get valuation rate from previous SLE or Item master, if item does not have the # allow zero valuration rate flag set - if not self.valuation_rate and sle.voucher_detail_no: + if not self.wh_data.valuation_rate and sle.voucher_detail_no: allow_zero_valuation_rate = self.check_if_allow_zero_valuation_rate(sle.voucher_type, sle.voucher_detail_no) if not allow_zero_valuation_rate: - self.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, + self.wh_data.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, sle.voucher_type, sle.voucher_no, self.allow_zero_rate, currency=erpnext.get_company_currency(sle.company)) @@ -329,22 +510,22 @@ class update_entries_after(object): outgoing_rate = flt(sle.outgoing_rate) if actual_qty > 0: - if not self.stock_queue: - self.stock_queue.append([0, 0]) + if not self.wh_data.stock_queue: + self.wh_data.stock_queue.append([0, 0]) # last row has the same rate, just updated the qty - if self.stock_queue[-1][1]==incoming_rate: - self.stock_queue[-1][0] += actual_qty + if self.wh_data.stock_queue[-1][1]==incoming_rate: + self.wh_data.stock_queue[-1][0] += actual_qty else: - if self.stock_queue[-1][0] > 0: - self.stock_queue.append([actual_qty, incoming_rate]) + if self.wh_data.stock_queue[-1][0] > 0: + self.wh_data.stock_queue.append([actual_qty, incoming_rate]) else: - qty = self.stock_queue[-1][0] + actual_qty - self.stock_queue[-1] = [qty, incoming_rate] + qty = self.wh_data.stock_queue[-1][0] + actual_qty + self.wh_data.stock_queue[-1] = [qty, incoming_rate] else: qty_to_pop = abs(actual_qty) while qty_to_pop: - if not self.stock_queue: + if not self.wh_data.stock_queue: # Get valuation rate from last sle if exists or from valuation rate field in item master allow_zero_valuation_rate = self.check_if_allow_zero_valuation_rate(sle.voucher_type, sle.voucher_detail_no) if not allow_zero_valuation_rate: @@ -354,35 +535,35 @@ class update_entries_after(object): else: _rate = 0 - self.stock_queue.append([0, _rate]) + self.wh_data.stock_queue.append([0, _rate]) index = None if outgoing_rate > 0: # Find the entry where rate matched with outgoing rate - for i, v in enumerate(self.stock_queue): + for i, v in enumerate(self.wh_data.stock_queue): if v[1] == outgoing_rate: index = i break # If no entry found with outgoing rate, collapse stack if index == None: - new_stock_value = sum((d[0]*d[1] for d in self.stock_queue)) - qty_to_pop*outgoing_rate - new_stock_qty = sum((d[0] for d in self.stock_queue)) - qty_to_pop - self.stock_queue = [[new_stock_qty, new_stock_value/new_stock_qty if new_stock_qty > 0 else outgoing_rate]] + new_stock_value = sum((d[0]*d[1] for d in self.wh_data.stock_queue)) - qty_to_pop*outgoing_rate + new_stock_qty = sum((d[0] for d in self.wh_data.stock_queue)) - qty_to_pop + self.wh_data.stock_queue = [[new_stock_qty, new_stock_value/new_stock_qty if new_stock_qty > 0 else outgoing_rate]] break else: index = 0 # select first batch or the batch with same rate - batch = self.stock_queue[index] + batch = self.wh_data.stock_queue[index] if qty_to_pop >= batch[0]: # consume current batch qty_to_pop = qty_to_pop - batch[0] - self.stock_queue.pop(index) - if not self.stock_queue and qty_to_pop: + self.wh_data.stock_queue.pop(index) + if not self.wh_data.stock_queue and qty_to_pop: # stock finished, qty still remains to be withdrawn # negative stock, keep in as a negative batch - self.stock_queue.append([-qty_to_pop, outgoing_rate or batch[1]]) + self.wh_data.stock_queue.append([-qty_to_pop, outgoing_rate or batch[1]]) break else: @@ -391,14 +572,14 @@ class update_entries_after(object): batch[0] = batch[0] - qty_to_pop qty_to_pop = 0 - stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in self.stock_queue)) - stock_qty = sum((flt(batch[0]) for batch in self.stock_queue)) + stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in self.wh_data.stock_queue)) + stock_qty = sum((flt(batch[0]) for batch in self.wh_data.stock_queue)) if stock_qty: - self.valuation_rate = stock_value / flt(stock_qty) + self.wh_data.valuation_rate = stock_value / flt(stock_qty) - if not self.stock_queue: - self.stock_queue.append([0, sle.incoming_rate or sle.outgoing_rate or self.valuation_rate]) + if not self.wh_data.stock_queue: + self.wh_data.stock_queue.append([0, sle.incoming_rate or sle.outgoing_rate or self.wh_data.valuation_rate]) def check_if_allow_zero_valuation_rate(self, voucher_type, voucher_detail_no): ref_item_dt = "" @@ -413,39 +594,56 @@ class update_entries_after(object): else: return 0 - def get_sle_before_datetime(self): + def get_sle_before_datetime(self, args): """get previous stock ledger entry before current time-bucket""" - if self.args.get('sle_id'): - self.args['name'] = self.args.get('sle_id') + sle = get_stock_ledger_entries(args, "<", "desc", "limit 1", for_update=False) + sle = sle[0] if sle else frappe._dict() + return sle - return get_stock_ledger_entries(self.args, "<=", "desc", "limit 1", for_update=False) - - def get_sle_after_datetime(self): + def get_sle_after_datetime(self, args): """get Stock Ledger Entries after a particular datetime, for reposting""" - return get_stock_ledger_entries(self.previous_sle or frappe._dict({ - "item_code": self.args.get("item_code"), "warehouse": self.args.get("warehouse") }), - ">", "asc", for_update=True, check_serial_no=False) + return get_stock_ledger_entries(args, ">", "asc", for_update=True, check_serial_no=False) def raise_exceptions(self): - deficiency = min(e["diff"] for e in self.exceptions) + msg_list = [] + for warehouse, exceptions in iteritems(self.exceptions): + deficiency = min(e["diff"] for e in exceptions) - if ((self.exceptions[0]["voucher_type"], self.exceptions[0]["voucher_no"]) in - frappe.local.flags.currently_saving): + if ((exceptions[0]["voucher_type"], exceptions[0]["voucher_no"]) in + frappe.local.flags.currently_saving): - msg = _("{0} units of {1} needed in {2} to complete this transaction.").format( - abs(deficiency), frappe.get_desk_link('Item', self.item_code), - frappe.get_desk_link('Warehouse', self.warehouse)) - else: - msg = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format( - abs(deficiency), frappe.get_desk_link('Item', self.item_code), - frappe.get_desk_link('Warehouse', self.warehouse), - self.exceptions[0]["posting_date"], self.exceptions[0]["posting_time"], - frappe.get_desk_link(self.exceptions[0]["voucher_type"], self.exceptions[0]["voucher_no"])) + msg = _("{0} units of {1} needed in {2} to complete this transaction.").format( + abs(deficiency), frappe.get_desk_link('Item', self.item_code), + frappe.get_desk_link('Warehouse', warehouse)) + else: + msg = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format( + abs(deficiency), frappe.get_desk_link('Item', self.item_code), + frappe.get_desk_link('Warehouse', warehouse), + exceptions[0]["posting_date"], exceptions[0]["posting_time"], + frappe.get_desk_link(exceptions[0]["voucher_type"], exceptions[0]["voucher_no"])) - if self.verbose: - frappe.throw(msg, NegativeStockError, title='Insufficient Stock') - else: - raise NegativeStockError(msg) + if msg: + msg_list.append(msg) + + if msg_list: + message = "\n\n".join(msg_list) + if self.verbose: + frappe.throw(message, NegativeStockError, title='Insufficient Stock') + else: + raise NegativeStockError(message) + + def update_bin(self): + # update bin for each warehouse + for warehouse, data in iteritems(self.data): + bin_doc = get_bin(self.item_code, warehouse) + + bin_doc.update({ + "valuation_rate": data.valuation_rate, + "actual_qty": data.qty_after_transaction, + "stock_value": data.stock_value + }) + bin_doc.flags.via_stock_ledger_entry = True + bin_doc.save(ignore_permissions=True) def get_previous_sle(args, for_update=False): """ @@ -489,6 +687,7 @@ def get_stock_ledger_entries(previous_sle, operator=None, select *, timestamp(posting_date, posting_time) as "timestamp" from `tabStock Ledger Entry` where item_code = %%(item_code)s + and is_cancelled = 0 %(conditions)s order by timestamp(posting_date, posting_time) %(order)s, creation %(order)s %(limit)s %(for_update)s""" % { @@ -498,10 +697,11 @@ def get_stock_ledger_entries(previous_sle, operator=None, "order": order }, previous_sle, as_dict=1, debug=debug) -def get_sle_by_id(sle_id): - return frappe.db.get_all('Stock Ledger Entry', - fields=['*', 'timestamp(posting_date, posting_time) as timestamp'], - filters={'name': sle_id})[0] +def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None): + return frappe.db.get_value('Stock Ledger Entry', + {'voucher_detail_no': voucher_detail_no, 'name': ['!=', excluded_sle]}, + ['item_code', 'warehouse', 'posting_date', 'posting_time', 'timestamp(posting_date, posting_time) as timestamp'], + as_dict=1) def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, allow_zero_rate=False, currency=None, company=None, raise_error_if_no_rate=True): @@ -529,7 +729,7 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, voucher_no, voucher_type)) if last_valuation_rate: - return flt(last_valuation_rate[0][0]) # as there is previous records, it might come with zero rate + return flt(last_valuation_rate[0][0]) # If negative stock allowed, and item delivered without any incoming entry, # system does not found any SLE, then take valuation rate from Item @@ -561,3 +761,54 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, frappe.throw(msg=msg, title=_("Valuation Rate Missing")) return valuation_rate + +def update_qty_in_future_sle(args, allow_negative_stock=None): + frappe.db.sql(""" + update `tabStock Ledger Entry` + set qty_after_transaction = qty_after_transaction + {qty} + where + item_code = %(item_code)s + and warehouse = %(warehouse)s + and voucher_no != %(voucher_no)s + and is_cancelled = 0 + and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s) + or ( + timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s) + and creation > %(creation)s + ) + ) + """.format(qty=args.actual_qty), args) + + validate_negative_qty_in_future_sle(args, allow_negative_stock) + +def validate_negative_qty_in_future_sle(args, allow_negative_stock=None): + allow_negative_stock = allow_negative_stock \ + or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) + + if args.actual_qty < 0 and not allow_negative_stock: + sle = get_future_sle_with_negative_qty(args) + if sle: + message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format( + abs(sle[0]["qty_after_transaction"]), + frappe.get_desk_link('Item', args.item_code), + frappe.get_desk_link('Warehouse', args.warehouse), + sle[0]["posting_date"], sle[0]["posting_time"], + frappe.get_desk_link(sle[0]["voucher_type"], sle[0]["voucher_no"])) + + frappe.throw(message, NegativeStockError, title='Insufficient Stock') + +def get_future_sle_with_negative_qty(args): + return frappe.db.sql(""" + select + qty_after_transaction, posting_date, posting_time, + voucher_type, voucher_no + from `tabStock Ledger Entry` + where + item_code = %(item_code)s + and warehouse = %(warehouse)s + and voucher_no != %(voucher_no)s + and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s) + and is_cancelled = 0 + and qty_after_transaction < 0 + limit 1 + """, args, as_dict=1) \ No newline at end of file diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index f9ac25443e..4ea7e4fcd6 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -63,6 +63,7 @@ def get_stock_value_on(warehouse=None, posting_date=None, item_code=None): SELECT item_code, stock_value, name, warehouse FROM `tabStock Ledger Entry` sle WHERE posting_date <= %s {0} + and is_cancelled = 0 ORDER BY timestamp(posting_date, posting_time) DESC, creation DESC """.format(condition), values, as_dict=1) @@ -211,7 +212,7 @@ def get_incoming_rate(args, raise_error_if_no_rate=True): currency=erpnext.get_company_currency(args.get('company')), company=args.get('company'), raise_error_if_no_rate=raise_error_if_no_rate) - return in_rate + return flt(in_rate) def get_avg_purchase_rate(serial_nos): """get average value of serial numbers""" @@ -375,4 +376,10 @@ def get_incoming_outgoing_rate_for_cancel(item_code, voucher_type, voucher_no, v outgoing_rate = outgoing_rate[0][0] if outgoing_rate else 0.0 - return outgoing_rate \ No newline at end of file + return outgoing_rate + +def is_reposting_item_valuation_in_progress(): + reposting_in_progress = frappe.db.exists("Repost Item Valuation", + {'docstatus': 1, 'status': ['in', ['Queued','In Progress']]}) + if reposting_in_progress: + frappe.msgprint(_("Item valuation reposting in progress. Report might show incorrect item valuation."), alert=1) \ No newline at end of file From 9466e42e7095f7f4ff32230ab7dace6642455ba9 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Mon, 21 Dec 2020 20:52:20 +0530 Subject: [PATCH 314/728] fix: change request modifications --- .../v13_0/update_project_template_tasks.py | 8 +- erpnext/projects/doctype/project/project.py | 51 +++++------ .../projects/doctype/project/test_project.py | 87 ++++++++++--------- .../project_template/project_template.py | 5 +- erpnext/projects/doctype/task/task.json | 4 +- erpnext/projects/doctype/task/test_task.py | 2 +- 6 files changed, 82 insertions(+), 75 deletions(-) diff --git a/erpnext/patches/v13_0/update_project_template_tasks.py b/erpnext/patches/v13_0/update_project_template_tasks.py index 1303efd93f..26c4259281 100644 --- a/erpnext/patches/v13_0/update_project_template_tasks.py +++ b/erpnext/patches/v13_0/update_project_template_tasks.py @@ -6,7 +6,13 @@ import frappe def execute(): frappe.reload_doc("projects", "doctype", "project_template") - for template_name in frappe.db.sql(""" select name from `tabProject Template` """, as_dict=1): + for template_name in frappe.db.sql(""" + select + name + from + `tabProject Template` """, + as_dict=1): + template = frappe.get_doc("Project Template", template_name.name) replace_tasks = False new_tasks = [] diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 13e72fec8a..2cdfb7af44 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -59,8 +59,8 @@ class Project(Document): for task in template.tasks: template_task_details = frappe.get_doc("Task", task.task) tmp_task_details.append(template_task_details) - project_tasks.append(self.create_task_from_template(template_task_details)) - + task = self.create_task_from_template(template_task_details) + project_tasks.append(task) self.dependency_mapping(tmp_task_details, project_tasks) def create_task_from_template(self, task_details): @@ -75,36 +75,33 @@ class Project(Document): task_weight = task_details.task_weight, type = task_details.type, issue = task_details.issue, - is_group = task_details.is_group, - start = task_details.start, - duration = task_details.duration + is_group = task_details.is_group )).insert() def dependency_mapping(self, template_tasks, project_tasks): - for tmp_task in template_tasks: - prj_task = list(filter(lambda x: x.subject == tmp_task.subject, project_tasks))[0] - prj_task = frappe.get_doc("Task", prj_task.name) - self.check_depends_on_value(tmp_task, prj_task, project_tasks) - self.check_for_parent_tasks(tmp_task, prj_task, project_tasks) + for template_task in template_tasks: + project_task = list(filter(lambda x: x.subject == template_task.subject, project_tasks))[0] + if template_task.get("depends_on") and not project_task.get("depends_on"): + self.check_depends_on_value(template_task, project_task, project_tasks) + if template_task.get("parent_task") and not project_task.get("parent_task"): + self.check_for_parent_tasks(template_task, project_task, project_tasks) - def check_depends_on_value(self, tmp_task, prj_task, project_tasks): - if tmp_task.get("depends_on") and not prj_task.get("depends_on"): - for child_task in tmp_task.get("depends_on"): - child_task_subject = frappe.db.get_value("Task", child_task.task, "subject") - corresponding_prj_task = list(filter(lambda x: x.subject == child_task_subject, project_tasks)) - if len(corresponding_prj_task): - prj_task.append("depends_on",{ - "task": corresponding_prj_task[0].name - }) - prj_task.save() + def check_depends_on_value(self, template_task, project_task, project_tasks): + for child_task in template_task.get("depends_on"): + child_task_subject = frappe.db.get_value("Task", child_task.task, "subject") + corresponding_project_task = list(filter(lambda x: x.subject == child_task_subject, project_tasks)) + if len(corresponding_project_task): + project_task.append("depends_on",{ + "task": corresponding_project_task[0].name + }) + project_task.save() - def check_for_parent_tasks(self, tmp_task, prj_task, project_tasks): - if tmp_task.get("parent_task") and not prj_task.get("parent_task"): - parent_task_subject = frappe.db.get_value("Task", tmp_task.get("parent_task"), "subject") - corresponding_prj_task = list(filter(lambda x: x.subject == parent_task_subject, project_tasks)) - if len(corresponding_prj_task): - prj_task.parent_task = corresponding_prj_task[0].name - prj_task.save() + def check_for_parent_tasks(self, template_task, project_task, project_tasks): + parent_task_subject = frappe.db.get_value("Task", template_task.get("parent_task"), "subject") + corresponding_project_task = list(filter(lambda x: x.subject == parent_task_subject, project_tasks)) + if len(corresponding_project_task): + project_task.parent_task = corresponding_project_task[0].name + project_task.save() def is_row_updated(self, row, existing_task_data, fields): if self.get("__islocal") or not existing_task_data: return True diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py index ce56a50b4e..1d2980ce46 100644 --- a/erpnext/projects/doctype/project/test_project.py +++ b/erpnext/projects/doctype/project/test_project.py @@ -17,78 +17,79 @@ class TestProject(unittest.TestCase): """ Test Action: Basic Test of a Project created from template. The template has a single task. """ - frappe.db.sql('delete from tabTask where project = "Test Project with Templ - no parent and dependend tasks"') - frappe.delete_doc('Project', 'Test Project with Templ - no parent and dependend tasks') + project_name = "Test Project with Template - No Parent and Dependend Tasks" + frappe.db.sql(""" delete from tabTask where project = %s """, project_name) + frappe.delete_doc('Project', project_name) - task1 = task_exists("Test Temp Task with no parent and dependency") + task1 = task_exists("Test Template Task with No Parent and Dependency") if not task1: - task1 = create_task(subject="Test Temp Task with no parent and dependency", is_template=1, begin=5, duration=3) + task1 = create_task(subject="Test Template Task with No Parent and Dependency", is_template=1, begin=5, duration=3) - template = make_project_template("Test Project Template - no parent and dependend tasks", [task1]) - project = get_project("Test Project with Templ - no parent and dependend tasks", template) - tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc') + template = make_project_template("Test Project Template - No Parent and Dependend Tasks", [task1]) + project = get_project(project_name, template) + tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks'], dict(project=project.name), order_by='creation asc') - self.assertEqual(tasks[0].subject, 'Test Temp Task with no parent and dependency') - self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, tasks[0])) + self.assertEqual(tasks[0].subject, 'Test Template Task with No Parent and Dependency') + self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 5, 3)) self.assertEqual(len(tasks), 1) def test_project_template_having_parent_child_tasks(self): + project_name = "Test Project with Template - Tasks with Parent-Child Relation" + frappe.db.sql(""" delete from tabTask where project = %s """, project_name) + frappe.delete_doc('Project', project_name) - frappe.db.sql('delete from tabTask where project = "Test Project with Templ - tasks with parent-child"') - frappe.delete_doc('Project', 'Test Project with Templ - tasks with parent-child') - - task1 = task_exists("Test Temp Task parent") + task1 = task_exists("Test Template Task Parent") if not task1: - task1 = create_task(subject="Test Temp Task parent", is_group=1, is_template=1, begin=1, duration=1) + task1 = create_task(subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=1) - task2 = task_exists("Test Temp Task child 1") + task2 = task_exists("Test Template Task Child 1") if not task2: - task2 = create_task(subject="Test Temp Task child 1", parent_task=task1.name, is_template=1, begin=1, duration=3) + task2 = create_task(subject="Test Template Task Child 1", parent_task=task1.name, is_template=1, begin=1, duration=3) - task3 = task_exists("Test Temp Task child 2") + task3 = task_exists("Test Template Task Child 2") if not task3: - task3 = create_task(subject="Test Temp Task child 2", parent_task=task1.name, is_template=1, begin=2, duration=3) + task3 = create_task(subject="Test Template Task Child 2", parent_task=task1.name, is_template=1, begin=2, duration=3) - template = make_project_template("Test Project Template - tasks with parent-child", [task1, task2, task3]) - project = get_project("Test Project with Templ - tasks with parent-child", template) - tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc') + template = make_project_template("Test Project Template - Tasks with Parent-Child Relation", [task1, task2, task3]) + project = get_project(project_name, template) + tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name'], dict(project=project.name), order_by='creation asc') - self.assertEqual(tasks[0].subject, 'Test Temp Task parent') - self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, tasks[0])) + self.assertEqual(tasks[0].subject, 'Test Template Task Parent') + self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 1, 1)) - self.assertEqual(tasks[1].subject, 'Test Temp Task child 1') - self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, tasks[1])) + self.assertEqual(tasks[1].subject, 'Test Template Task Child 1') + self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 1, 3)) self.assertEqual(tasks[1].parent_task, tasks[0].name) - self.assertEqual(tasks[2].subject, 'Test Temp Task child 2') - self.assertEqual(getdate(tasks[2].exp_end_date), calculate_end_date(project, tasks[2])) + self.assertEqual(tasks[2].subject, 'Test Template Task Child 2') + self.assertEqual(getdate(tasks[2].exp_end_date), calculate_end_date(project, 2, 3)) self.assertEqual(tasks[2].parent_task, tasks[0].name) self.assertEqual(len(tasks), 3) def test_project_template_having_dependent_tasks(self): + project_name = "Test Project with Template - Dependent Tasks" + frappe.db.sql(""" delete from tabTask where project = %s """, project_name) + frappe.delete_doc('Project', project_name) - frappe.db.sql('delete from tabTask where project = "Test Project with Templ - dependent tasks"') - frappe.delete_doc('Project', 'Test Project with Templ - dependent tasks') - - task1 = task_exists("Test Temp Task for dependency") + task1 = task_exists("Test Template Task for Dependency") if not task1: - task1 = create_task(subject="Test Temp Task for dependency", is_template=1, begin=3, duration=1) + task1 = create_task(subject="Test Template Task for Dependency", is_template=1, begin=3, duration=1) - task2 = task_exists("Test Temp Task with dependency") + task2 = task_exists("Test Template Task with Dependency") if not task2: - task2 = create_task(subject="Test Temp Task with dependency", depends_on=task1.name, is_template=1, begin=2, duration=2) + task2 = create_task(subject="Test Template Task with Dependency", depends_on=task1.name, is_template=1, begin=2, duration=2) - template = make_project_template("Test Project with Templ - dependent tasks", [task1, task2]) - project = get_project("Test Project with Templ - dependent tasks", template) - tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc') + template = make_project_template("Test Project with Template - Dependent Tasks", [task1, task2]) + project = get_project(project_name, template) + tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name'], dict(project=project.name), order_by='creation asc') - self.assertEqual(tasks[1].subject, 'Test Temp Task with dependency') - self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, tasks[1])) + self.assertEqual(tasks[1].subject, 'Test Template Task with Dependency') + self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 2, 2)) self.assertTrue(tasks[1].depends_on_tasks.find(tasks[0].name) >= 0 ) - self.assertEqual(tasks[0].subject, 'Test Temp Task for dependency') - self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, tasks[0]) ) + self.assertEqual(tasks[0].subject, 'Test Template Task for Dependency') + self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 3, 1) ) self.assertEqual(len(tasks), 2) @@ -129,5 +130,5 @@ def task_exists(subject): return False return frappe.get_doc("Task", result[0].name) -def calculate_end_date(project, task): - return getdate(add_days(project.expected_start_date, task.start + task.duration)) \ No newline at end of file +def calculate_end_date(project, start, duration): + return getdate(add_days(project.expected_start_date, start + duration)) \ No newline at end of file diff --git a/erpnext/projects/doctype/project_template/project_template.py b/erpnext/projects/doctype/project_template/project_template.py index 1beebf7a25..aace40240c 100644 --- a/erpnext/projects/doctype/project_template/project_template.py +++ b/erpnext/projects/doctype/project_template/project_template.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ +from frappe.utils import get_link_to_form class ProjectTemplate(Document): @@ -18,8 +19,8 @@ class ProjectTemplate(Document): if task_details.depends_on: for dependency_task in task_details.depends_on: if not self.check_dependent_task_presence(dependency_task.task): - task_details_format = """{0}""".format(task_details.name) - dependency_task_format = """{0}""".format(dependency_task.task) + task_details_format = get_link_to_form("Task",task_details.name) + dependency_task_format = get_link_to_form("Task", dependency_task.task) frappe.throw(_("Task {0} depends on Task {1}. Please add Task {1} to the Tasks list.").format(frappe.bold(task_details_format), frappe.bold(dependency_task_format))) def check_dependent_task_presence(self, task): diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index a9e3d9bc0f..bb55256f7d 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -371,11 +371,13 @@ "label": "Is Template" }, { + "depends_on": "is_template", "fieldname": "start", "fieldtype": "Int", "label": "Begin On (Days)" }, { + "depends_on": "is_template", "fieldname": "duration", "fieldtype": "Int", "label": "Duration (Days)" @@ -386,7 +388,7 @@ "is_tree": 1, "links": [], "max_attachments": 5, - "modified": "2020-12-07 13:26:53.614689", + "modified": "2020-12-21 11:59:24.196834", "modified_by": "Administrator", "module": "Projects", "name": "Task", diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py index aded78b857..25714f8cde 100644 --- a/erpnext/projects/doctype/task/test_task.py +++ b/erpnext/projects/doctype/task/test_task.py @@ -104,7 +104,7 @@ def create_task(subject, start=None, end=None, depends_on=None, project=None, pa task.subject = subject task.exp_start_date = start or nowdate() task.exp_end_date = end or nowdate() - task.project = project or "_Test Project" + task.project = project or None if is_template else "_Test Project" task.is_template = is_template task.start = begin task.duration = duration From c36cab81f229376cbdde96cf7cfe4ccbd33b6f36 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Mon, 21 Dec 2020 23:46:02 +0530 Subject: [PATCH 315/728] fix: Update year_to_date and month_to_date field labels to show company currency --- .../doctype/salary_slip/salary_slip.js | 6 +++--- .../doctype/salary_slip/salary_slip.json | 20 +++++++++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js index f7e22c6387..5694871762 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.js +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js @@ -125,15 +125,15 @@ frappe.ui.form.on("Salary Slip", { 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"], + "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date"], company_currency); - frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words"], + frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words", "year_to_date", "month_to_date"], 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"], + "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date"], frm.doc.currency != company_currency); }, diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json index d981a39953..43deee43aa 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.json +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json @@ -70,10 +70,12 @@ "net_pay", "base_net_pay", "year_to_date", + "base_year_to_date", "column_break_53", "rounded_total", "base_rounded_total", "month_to_date", + "base_month_to_date", "section_break_55", "total_in_words", "column_break_69", @@ -584,12 +586,26 @@ { "fieldname": "year_to_date", "fieldtype": "Currency", + "label": "Year To Date", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "month_to_date", + "fieldtype": "Currency", + "label": "Month To Date", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "base_year_to_date", + "fieldtype": "Currency", "label": "Year To Date(Company Currency)", "options": "Company:company:default_currency", "read_only": 1 }, { - "fieldname": "month_to_date", + "fieldname": "base_month_to_date", "fieldtype": "Currency", "label": "Month To Date(Company Currency)", "options": "Company:company:default_currency", @@ -600,7 +616,7 @@ "idx": 9, "is_submittable": 1, "links": [], - "modified": "2020-12-18 23:57:41.042954", + "modified": "2020-12-21 23:43:44.959840", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Slip", From 090783804bdc59f22b8a7afee43cb3ddabcd37b2 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Mon, 21 Dec 2020 23:52:05 +0530 Subject: [PATCH 316/728] fix: Improve month_to_date computation --- erpnext/payroll/doctype/salary_slip/salary_slip.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index e86a7fc315..02e5f2d1d1 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe, erpnext import datetime, math -from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words, formatdate +from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words, formatdate, get_first_day from frappe.model.naming import make_autoname from frappe import msgprint, _ @@ -1150,8 +1150,7 @@ class SalarySlip(TransactionBase): def compute_month_to_date(self): month_to_date = 0 - date = datetime.datetime.strptime(self.start_date,"%Y-%m-%d") - first_day_of_the_month = "1-" + str(date.month) + "-" + str(date.year) + first_day_of_the_month = get_first_day(self.start_date) salary_slips_from_this_month = frappe.get_list('Salary Slip', fields = ['employee_name', 'start_date', 'net_pay'], filters = {'employee_name' : self.employee_name, From fa102e4713be1eadc6b873c0f94ad3a44fe74c31 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 17 Nov 2020 16:34:05 +0530 Subject: [PATCH 317/728] feat: add templates for online store --- .../shopping_cart/web_template/__init__.py | 0 .../web_template/hero_slider/__init__.py | 0 .../web_template/hero_slider/hero_slider.html | 0 .../web_template/hero_slider/hero_slider.json | 142 ++++++++ .../web_template/item_card_group/__init__.py | 0 .../item_card_group/item_card_group.html | 0 .../item_card_group/item_card_group.json | 312 ++++++++++++++++++ .../web_template/product_card/__init__.py | 0 .../product_card/product_card.html | 0 .../product_card/product_card.json | 33 ++ .../product_category_cards/__init__.py | 0 .../product_category_cards.html | 0 .../product_category_cards.json | 95 ++++++ 13 files changed, 582 insertions(+) create mode 100644 erpnext/shopping_cart/web_template/__init__.py create mode 100644 erpnext/shopping_cart/web_template/hero_slider/__init__.py create mode 100644 erpnext/shopping_cart/web_template/hero_slider/hero_slider.html create mode 100644 erpnext/shopping_cart/web_template/hero_slider/hero_slider.json create mode 100644 erpnext/shopping_cart/web_template/item_card_group/__init__.py create mode 100644 erpnext/shopping_cart/web_template/item_card_group/item_card_group.html create mode 100644 erpnext/shopping_cart/web_template/item_card_group/item_card_group.json create mode 100644 erpnext/shopping_cart/web_template/product_card/__init__.py create mode 100644 erpnext/shopping_cart/web_template/product_card/product_card.html create mode 100644 erpnext/shopping_cart/web_template/product_card/product_card.json create mode 100644 erpnext/shopping_cart/web_template/product_category_cards/__init__.py create mode 100644 erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.html create mode 100644 erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.json diff --git a/erpnext/shopping_cart/web_template/__init__.py b/erpnext/shopping_cart/web_template/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/hero_slider/__init__.py b/erpnext/shopping_cart/web_template/hero_slider/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json new file mode 100644 index 0000000000..a8ce2cd3cb --- /dev/null +++ b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json @@ -0,0 +1,142 @@ +{ + "__unsaved": 1, + "creation": "2020-11-17 15:21:51.207221", + "docstatus": 0, + "doctype": "Web Template", + "fields": [ + { + "__unsaved": 1, + "fieldname": "slide_1_image", + "fieldtype": "Attach Image", + "label": "Image", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_1_title", + "fieldtype": "Data", + "label": "Title", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_1_subtitle", + "fieldtype": "Data", + "label": "Subtitle", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_1_primary_action_label", + "fieldtype": "Data", + "label": "Primary Action Label", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_1_primary_action", + "fieldtype": "Data", + "label": "Primary Action", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2", + "fieldtype": "Section Break", + "label": "Slide 2", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2_image", + "fieldtype": "Attach Image", + "label": "Image ", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2_title", + "fieldtype": "Data", + "label": "Title ", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2_subtitle", + "fieldtype": "Data", + "label": "Subtitle ", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2_primary_action_label", + "fieldtype": "Data", + "label": "Primary Action Label ", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2_primary_action", + "fieldtype": "Data", + "label": "Primary Action ", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3", + "fieldtype": "Section Break", + "label": "Slide 3", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_image", + "fieldtype": "Attach Image", + "label": "Image ", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_title", + "fieldtype": "Data", + "label": "Title ", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_subtitle", + "fieldtype": "Data", + "label": "Subtitle ", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_primary_action_label", + "fieldtype": "Data", + "label": "Primary Action Label ", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_primary_action", + "fieldtype": "Data", + "label": "Primary Action ", + "reqd": 0 + } + ], + "idx": 0, + "modified": "2020-11-17 15:23:48.584868", + "modified_by": "Administrator", + "module": "Shopping Cart", + "name": "Hero Slider", + "owner": "Administrator", + "standard": 1, + "template": "", + "type": "Section" +} \ No newline at end of file diff --git a/erpnext/shopping_cart/web_template/item_card_group/__init__.py b/erpnext/shopping_cart/web_template/item_card_group/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/item_card_group/item_card_group.json b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.json new file mode 100644 index 0000000000..f1a70a99ad --- /dev/null +++ b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.json @@ -0,0 +1,312 @@ +{ + "__unsaved": 1, + "creation": "2020-11-17 15:35:05.285322", + "docstatus": 0, + "doctype": "Web Template", + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "reqd": 1 + }, + { + "fieldname": "subtitle", + "fieldtype": "Data", + "label": "Subtitle", + "reqd": 0 + }, + { + "fieldname": "card_1", + "fieldtype": "Section Break", + "label": "Card 1", + "reqd": 0 + }, + { + "fieldname": "card_1_item", + "fieldtype": "Link", + "label": "Item", + "options": "Item", + "reqd": 0 + }, + { + "fieldname": "card_1_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_2", + "fieldtype": "Section Break", + "label": "Card 2", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_2_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_2_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_3", + "fieldtype": "Section Break", + "label": "Card 3", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_3_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_3_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_4", + "fieldtype": "Section Break", + "label": "Card 4", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_4_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_4_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_5", + "fieldtype": "Section Break", + "label": "Card 5", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_5_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_5_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_6", + "fieldtype": "Section Break", + "label": "Card 6", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_6_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_6_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_7", + "fieldtype": "Section Break", + "label": "Card 7", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_7_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_7_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_8", + "fieldtype": "Section Break", + "label": "Card 8", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_8_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_8_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_9", + "fieldtype": "Section Break", + "label": "Card 9", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_9_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_9_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_10", + "fieldtype": "Section Break", + "label": "Card 10", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_10_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_10_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_11", + "fieldtype": "Section Break", + "label": "Card 11", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_11_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_11_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_12", + "fieldtype": "Section Break", + "label": "Card 12", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_12_item", + "fieldtype": "Link", + "label": "Item", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "card_12_featured", + "fieldtype": "Check", + "label": "Featured", + "reqd": 0 + } + ], + "idx": 0, + "modified": "2020-11-17 16:37:46.325181", + "modified_by": "Administrator", + "module": "Shopping Cart", + "name": "Item Card Group", + "owner": "Administrator", + "standard": 1, + "template": "", + "type": "Section" +} \ No newline at end of file diff --git a/erpnext/shopping_cart/web_template/product_card/__init__.py b/erpnext/shopping_cart/web_template/product_card/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/product_card/product_card.html b/erpnext/shopping_cart/web_template/product_card/product_card.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/product_card/product_card.json b/erpnext/shopping_cart/web_template/product_card/product_card.json new file mode 100644 index 0000000000..1059c1b251 --- /dev/null +++ b/erpnext/shopping_cart/web_template/product_card/product_card.json @@ -0,0 +1,33 @@ +{ + "__unsaved": 1, + "creation": "2020-11-17 15:28:47.809342", + "docstatus": 0, + "doctype": "Web Template", + "fields": [ + { + "__unsaved": 1, + "fieldname": "item", + "fieldtype": "Link", + "label": "Item", + "options": "Item", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "featured", + "fieldtype": "Check", + "label": "Featured", + "options": "", + "reqd": 0 + } + ], + "idx": 0, + "modified": "2020-11-17 15:33:34.982515", + "modified_by": "Administrator", + "module": "Shopping Cart", + "name": "Product Card", + "owner": "Administrator", + "standard": 1, + "template": "", + "type": "Component" +} \ No newline at end of file diff --git a/erpnext/shopping_cart/web_template/product_category_cards/__init__.py b/erpnext/shopping_cart/web_template/product_category_cards/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.html b/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.json b/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.json new file mode 100644 index 0000000000..1a3143c209 --- /dev/null +++ b/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.json @@ -0,0 +1,95 @@ +{ + "__unsaved": 1, + "creation": "2020-11-17 15:25:50.855934", + "docstatus": 0, + "doctype": "Web Template", + "fields": [ + { + "__unsaved": 1, + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "reqd": 1 + }, + { + "__unsaved": 1, + "fieldname": "subtitle", + "fieldtype": "Data", + "label": "Subtitle", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_1_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_2_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_3_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_4_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_5_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_6_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_7_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "category_8_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "reqd": 0 + } + ], + "idx": 0, + "modified": "2020-11-17 15:27:36.250460", + "modified_by": "Administrator", + "module": "Shopping Cart", + "name": "Product Category Cards", + "owner": "Administrator", + "standard": 1, + "template": "", + "type": "Section" +} \ No newline at end of file From 09c8aaa87aa40a1a7dc287cddcfb2613938f8446 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 18 Nov 2020 17:09:45 +0530 Subject: [PATCH 318/728] feat: add slider template --- .../web_template/hero_slider/hero_slider.html | 112 +++++++++ .../web_template/hero_slider/hero_slider.json | 230 ++++++++++++++++-- 2 files changed, 323 insertions(+), 19 deletions(-) diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html index e69de29bb2..e8b79743ab 100644 --- a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html +++ b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html @@ -0,0 +1,112 @@ +{%- macro slide(image, title, subtitle, action, label, index, align="Left", theme="Dark") -%} +{%- set align_class = resolve_class({ + 'text-right': align == 'Right', + 'text-centre': align == 'Center', + 'text-left': align == 'Left', +}) -%} + +{%- set heading_class = resolve_class({ + 'text-white': theme == 'Dark', + '': theme == 'Light', +}) -%} + +{%- endmacro -%} + + + + + + \ No newline at end of file diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json index a8ce2cd3cb..496fc7995e 100644 --- a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json +++ b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json @@ -5,35 +5,56 @@ "doctype": "Web Template", "fields": [ { - "__unsaved": 1, + "fieldname": "slider_name", + "fieldtype": "Data", + "label": "Slider Name", + "reqd": 1 + }, + { + "default": "1", + "fieldname": "show_indicators", + "fieldtype": "Check", + "label": "Show Indicators", + "reqd": 0 + }, + { + "default": "1", + "fieldname": "show_controls", + "fieldtype": "Check", + "label": "Show Controls", + "reqd": 0 + }, + { + "fieldname": "slide_1", + "fieldtype": "Section Break", + "label": "Slide 1", + "reqd": 0 + }, + { "fieldname": "slide_1_image", "fieldtype": "Attach Image", "label": "Image", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_1_title", "fieldtype": "Data", "label": "Title", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_1_subtitle", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Subtitle", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_1_primary_action_label", "fieldtype": "Data", "label": "Primary Action Label", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_1_primary_action", "fieldtype": "Data", "label": "Primary Action", @@ -41,46 +62,73 @@ }, { "__unsaved": 1, + "fieldname": "slide_1_content_align", + "fieldtype": "Select", + "label": "Content Align", + "options": "Left\nCentre\nRight", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_1_theme", + "fieldtype": "Select", + "label": "Slide Theme", + "options": "Dark\nLight", + "reqd": 0 + }, + { "fieldname": "slide_2", "fieldtype": "Section Break", "label": "Slide 2", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_2_image", "fieldtype": "Attach Image", "label": "Image ", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_2_title", "fieldtype": "Data", "label": "Title ", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_2_subtitle", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Subtitle ", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_2_primary_action_label", "fieldtype": "Data", "label": "Primary Action Label ", "reqd": 0 }, { - "__unsaved": 1, "fieldname": "slide_2_primary_action", "fieldtype": "Data", "label": "Primary Action ", "reqd": 0 }, + { + "__unsaved": 1, + "default": "Left", + "fieldname": "slide_2_content_align", + "fieldtype": "Select", + "label": "Content Align", + "options": "Left\nCentre\nRight", + "reqd": 0 + }, + { + "__unsaved": 1, + "fieldname": "slide_2_theme", + "fieldtype": "Select", + "label": "Slide Theme", + "options": "Dark\nLight", + "reqd": 0 + }, { "__islocal": 1, "__unsaved": 1, @@ -94,7 +142,7 @@ "__unsaved": 1, "fieldname": "slide_3_image", "fieldtype": "Attach Image", - "label": "Image ", + "label": "Image", "reqd": 0 }, { @@ -102,15 +150,15 @@ "__unsaved": 1, "fieldname": "slide_3_title", "fieldtype": "Data", - "label": "Title ", + "label": "Title", "reqd": 0 }, { "__islocal": 1, "__unsaved": 1, "fieldname": "slide_3_subtitle", - "fieldtype": "Data", - "label": "Subtitle ", + "fieldtype": "Small Text", + "label": "Subtitle", "reqd": 0 }, { @@ -118,7 +166,7 @@ "__unsaved": 1, "fieldname": "slide_3_primary_action_label", "fieldtype": "Data", - "label": "Primary Action Label ", + "label": "Primary Action Label", "reqd": 0 }, { @@ -126,12 +174,156 @@ "__unsaved": 1, "fieldname": "slide_3_primary_action", "fieldtype": "Data", - "label": "Primary Action ", + "label": "Primary Action", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_content_align", + "fieldtype": "Select", + "label": "Content Align", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_3_theme", + "fieldtype": "Select", + "label": "Slide Theme", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4", + "fieldtype": "Section Break", + "label": "Slide 4", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_image", + "fieldtype": "Attach Image", + "label": "Image", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_title", + "fieldtype": "Data", + "label": "Title", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_subtitle", + "fieldtype": "Small Text", + "label": "Subtitle", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_primary_action_label", + "fieldtype": "Data", + "label": "Primary Action Label", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_primary_action", + "fieldtype": "Data", + "label": "Primary Action", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_content_align", + "fieldtype": "Select", + "label": "Content Align", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_4_theme", + "fieldtype": "Select", + "label": "Slide Theme", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5", + "fieldtype": "Section Break", + "label": "Slide 5", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_image", + "fieldtype": "Attach Image", + "label": "Image", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_title", + "fieldtype": "Data", + "label": "Title", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_subtitle", + "fieldtype": "Small Text", + "label": "Subtitle", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_primary_action_label", + "fieldtype": "Data", + "label": "Primary Action Label", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_primary_action", + "fieldtype": "Data", + "label": "Primary Action", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_content_align", + "fieldtype": "Select", + "label": "Content Align", + "reqd": 0 + }, + { + "__islocal": 1, + "__unsaved": 1, + "fieldname": "slide_5_theme", + "fieldtype": "Select", + "label": "Slide Theme", "reqd": 0 } ], "idx": 0, - "modified": "2020-11-17 15:23:48.584868", + "modified": "2020-11-18 17:09:31.323198", "modified_by": "Administrator", "module": "Shopping Cart", "name": "Hero Slider", From e40926b4cc3fb9c678fd901007cc84f6881b1f43 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 18 Nov 2020 17:10:02 +0530 Subject: [PATCH 319/728] fix: don't use primary action for create --- erpnext/stock/doctype/item/item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 9e0941146a..2d5ab5ac69 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -81,7 +81,7 @@ frappe.ui.form.on("Item", { }, __('Create')); } - frm.page.set_inner_btn_group_as_primary(__('Create')); + // frm.page.set_inner_btn_group_as_primary(__('Create')); } if (frm.doc.variant_of) { frm.set_intro(__('This Item is a Variant of {0} (Template).', From b4946c31b40903899effa767878cc5e0ac55216b Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 18 Nov 2020 17:39:17 +0530 Subject: [PATCH 320/728] refactor: use h1 for slidetitle --- erpnext/shopping_cart/web_template/hero_slider/hero_slider.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html index e8b79743ab..3f0419e564 100644 --- a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html +++ b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html @@ -14,7 +14,7 @@ {%- if title or subtitle -%} {%- endmacro -%} -
    +
    {%- if title -%}

    {{ title }}

    {%- endif -%} @@ -37,17 +37,4 @@
    diff --git a/erpnext/templates/generators/item/item.html b/erpnext/templates/generators/item/item.html index d3691a6e99..55c45e256a 100644 --- a/erpnext/templates/generators/item/item.html +++ b/erpnext/templates/generators/item/item.html @@ -7,17 +7,19 @@ {% endblock %} {% block page_content %} -{% from "erpnext/templates/includes/macros.html" import product_image %} -
    -
    -
    - {% include "templates/generators/item/item_image.html" %} - {% include "templates/generators/item/item_details.html" %} +
    + {% from "erpnext/templates/includes/macros.html" import product_image %} +
    +
    +
    + {% include "templates/generators/item/item_image.html" %} + {% include "templates/generators/item/item_details.html" %} +
    + + {% include "templates/generators/item/item_specifications.html" %} + + {{ doc.website_content or '' }}
    - - {% include "templates/generators/item/item_specifications.html" %} - - {{ doc.website_content or '' }}
    {% endblock %} diff --git a/erpnext/templates/generators/item/item_add_to_cart.html b/erpnext/templates/generators/item/item_add_to_cart.html index dbf15de1e4..3210e2497f 100644 --- a/erpnext/templates/generators/item/item_add_to_cart.html +++ b/erpnext/templates/generators/item/item_add_to_cart.html @@ -6,10 +6,10 @@
    {% if cart_settings.show_price and product_info.price %} -

    +
    {{ product_info.price.formatted_price_sales_uom }} - ({{ product_info.price.formatted_price }} / {{ product_info.uom }}) -

    + ({{ product_info.price.formatted_price }} / {{ product_info.uom }}) +
    {% else %} {{ _("Unit of Measurement") }} : {{ product_info.uom }} {% endif %} @@ -17,11 +17,11 @@ {% if cart_settings.show_stock_availability %}
    {% if product_info.in_stock == 0 %} - + {{ _('Not in stock') }} {% elif product_info.in_stock == 1 %} - + {{ _('In stock') }} {% if product_info.show_stock_qty and product_info.stock_qty %} ({{ product_info.stock_qty[0][0] }}) diff --git a/erpnext/templates/generators/item/item_details.html b/erpnext/templates/generators/item/item_details.html index 4cbecb0215..9a0bb5cfc3 100644 --- a/erpnext/templates/generators/item/item_details.html +++ b/erpnext/templates/generators/item/item_details.html @@ -1,14 +1,21 @@
    -

    +
    {{ item_name }} -

    -

    +

    +

    {{ _("Item Code") }}: {{ doc.name }}

    +{% if has_variants %} + + {% include "templates/generators/item/item_configure.html" %} +{% else %} + + {% include "templates/generators/item/item_add_to_cart.html" %} +{% endif %} -
    +
    {% if frappe.utils.strip_html(doc.web_long_description or '') %} {{ doc.web_long_description | safe }} {% elif frappe.utils.strip_html(doc.description or '') %} @@ -17,12 +24,4 @@ {{ _("No description given") }} {% endif %}
    - -{% if has_variants %} - - {% include "templates/generators/item/item_configure.html" %} -{% else %} - - {% include "templates/generators/item/item_add_to_cart.html" %} -{% endif %}
    diff --git a/erpnext/templates/generators/item/item_image.html b/erpnext/templates/generators/item/item_image.html index 5d46a45053..fa70ebe998 100644 --- a/erpnext/templates/generators/item/item_image.html +++ b/erpnext/templates/generators/item/item_image.html @@ -1,42 +1,42 @@ -
    -{% if slides %} -{{ product_image(slides[0].image, 'product-image') }} -
    - {% for item in slides %} - {{ item.heading }} - {% endfor %} -
    - - -{% else %} -{{ product_image(website_image or image or 'no-image.jpg', alt=website_image_alt or item_name) }} -{% endif %} + $('.item-slideshow-image').removeClass('active'); + $img.addClass('active'); + }); + }) + + {% else %} + {{ product_image(website_image or image or 'no-image.jpg', alt=website_image_alt or item_name) }} + {% endif %} - + - +
    ", - "line_breaks": 0, - "modified": "2018-10-08 14:56:56.912851", - "module": "Regional", - "name": "IRS 1099 Form", - "print_format_builder": 1, - "print_format_type": "Server", - "show_section_headings": 0, - "standard": "No" - } -] +{ + "align_labels_right": 0, + "creation": "2020-11-09 16:01:26.096002", + "css": "", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Supplier", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"
    \\t\\t\\t\\t

    TAX Invoice
    {{ doc.name }}\\t\\t\\t\\t

    \"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"customer_name\", \"label\": \"Customer Name\"}, {\"print_hide\": 0, \"fieldname\": \"customer_name_in_arabic\", \"label\": \"Customer Name in Arabic\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"posting_date\", \"label\": \"Date\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Address\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"print_hide\": 0, \"fieldname\": \"company_trn\", \"label\": \"Company TRN\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company_address_display\", \"label\": \"Company Address\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item_code\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"200px\"}, {\"print_hide\": 0, \"fieldname\": \"uom\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_code\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"items\", \"label\": \"Items\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"label\": \"Total\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"charge_type\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"row_id\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"account_head\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"cost_center\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_amount_after_discount_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"base_tax_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"base_total\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"base_tax_amount_after_discount_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"item_wise_tax_detail\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"taxes\", \"label\": \"Sales Taxes and Charges\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"grand_total\", \"label\": \"Grand Total\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\", \"label\": \"Rounded Total\"}, {\"print_hide\": 0, \"fieldname\": \"in_words\", \"align\": \"left\", \"label\": \"In Words\"}]", + "html": "
    \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n\n \n
    PAYER'S name, street address,\n city or town, state or province, country, ZIP
    or foreign postal code, and telephone no.
    \n {{ company or \"\" }}
    \n {{ payer_street_address or \"\" }}\n
    1 RentsOMB No. 1545-0115
    \n {{ fiscal_year[:2] }}\n {{ fiscal_year[-2:] }}
    Form 1099-MISC\n
    Miscellaneous Income
    2 Royalties
    3 Other Income
    {{ payments or \"\" }}
    4 Federal Income tax withheldCopy A
    For
    Internal Revenue
    Service\n Center

    File with Form 1096
    PAYER'S TIN
    {{ company_tin or \"\" }}
    RECIPIENT'S TIN

    {{ tax_id or \"None\" }}
    Fishing boat proceeds6 Medical and health care payments
    RECIPIENT'S name
    {{ supplier or \"\" }}
    7 Nonemployee compensation
    \n
    Substitute payments in lieu of dividends or interestFor Privacy Act
    and Paperwork
    Reduction Act
    Notice, see\n the
    2018 General
    Instructions for
    Certain
    Information
    Returns.
    Street address (including apt. no.)
    \n {{ recipient_street_address or \"\" }}\n
    $___________$___________
    9 Payer made direct sales of
    $5,000 or more of consumer\n products
    to a buyer
    (recipient) for resale
    10 Crop insurance proceeds
    City or town, state or province, country, and ZIP or\n foreign postal code
    \n {{ recipient_city_state or \"\" }}\n
    $___________
    1112
    Account number (see instructions)FACTA filing
    requirement
    2nd TIN not.13 Excess golden parachute payments
    $___________
    14 Gross proceeds paid to an
    attorney
    $___________
    15a Section 409A deferrals15b Section 409 income16 State tax withheld17 State/Payer's state no.18 State income
    $$$$
    Form 1099-MISC Cat. No. 14425J www.irs.gov/Form1099MISC Department of the\n Treasury - Internal Revenue Service
    \n
    \n
    \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {{ supplier or \"\" }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n\n \n
    PAYER'S name, street address,\n city or town, state or province, country, ZIP
    or foreign postal code, and telephone no.
    \n {{ company or \"\"}}\n {{ payer_street_address or \"\" }}\n
    1 RentsOMB No. 1545-0115
    \n {{ fiscal_year[:2] }}\n {{ fiscal_year[-2:] }}
    Form 1099-MISC\n
    Miscellaneous Income
    2 Royalties
    3 Other Income
    \n {{ payments or \"\" }}\n
    4 Federal Income tax withheldCopy 1
    For State Tax
    Department
    PAYER'S TIN
    \n {{ company_tin or \"\" }}\n
    RECIPIENT'S TIN
    \n {{ tax_id or \"\" }}\n
    Fishing boat proceeds6 Medical and health care payments
    RECIPIENT'S name7 Nonemployee compensation
    \n
    Substitute payments in lieu of dividends or interest
    Street address (including apt. no.)
    \n {{ recipient_street_address or \"\" }}\n
    $___________$___________
    9 Payer made direct sales of
    $5,000 or more of consumer\n products
    to a buyer
    (recipient) for resale
    10 Crop insurance proceeds
    City or town, state or province, country, and ZIP or\n foreign postal code
    \n {{ recipient_city_state or \"\" }}\n
    $___________
    1112
    Account number (see instructions)FACTA filing
    requirement
    2nd TIN not.13 Excess golden parachute payments
    $___________
    14 Gross proceeds paid to an
    attorney
    $___________
    15a Section 409A deferrals15b Section 409 income16 State tax withheld17 State/Payer's state no.18 State income
    $$$$
    Form 1099-MISC Cat. No. 14425J www.irs.gov/Form1099MISC Department of the\n Treasury - Internal Revenue Service
    \n
    \n", + "idx": 0, + "line_breaks": 0, + "modified": "2021-01-19 07:25:16.333666", + "modified_by": "Administrator", + "module": "Regional", + "name": "IRS 1099 Form", + "owner": "Administrator", + "print_format_builder": 1, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "No" +} \ No newline at end of file diff --git a/erpnext/regional/report/irs_1099/irs_1099.js b/erpnext/regional/report/irs_1099/irs_1099.js index 2d74652cfe..070ff43f78 100644 --- a/erpnext/regional/report/irs_1099/irs_1099.js +++ b/erpnext/regional/report/irs_1099/irs_1099.js @@ -4,7 +4,7 @@ frappe.query_reports["IRS 1099"] = { "filters": [ { - "fieldname":"company", + "fieldname": "company", "label": __("Company"), "fieldtype": "Link", "options": "Company", @@ -13,7 +13,7 @@ frappe.query_reports["IRS 1099"] = { "width": 80, }, { - "fieldname":"fiscal_year", + "fieldname": "fiscal_year", "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", @@ -22,7 +22,7 @@ frappe.query_reports["IRS 1099"] = { "width": 80, }, { - "fieldname":"supplier_group", + "fieldname": "supplier_group", "label": __("Supplier Group"), "fieldtype": "Link", "options": "Supplier Group", @@ -32,16 +32,16 @@ frappe.query_reports["IRS 1099"] = { }, ], - onload: function(query_report) { + onload: function (query_report) { query_report.page.add_inner_button(__("Print IRS 1099 Forms"), () => { build_1099_print(query_report); }); } }; -function build_1099_print(query_report){ +function build_1099_print(query_report) { let filters = JSON.stringify(query_report.get_values()); let w = window.open('/api/method/erpnext.regional.report.irs_1099.irs_1099.irs_1099_print?' + - '&filters=' + encodeURIComponent(filters)); + '&filters=' + encodeURIComponent(filters)); // w.print(); } diff --git a/erpnext/regional/report/irs_1099/irs_1099.py b/erpnext/regional/report/irs_1099/irs_1099.py index d3509e500f..c1c8aedc9f 100644 --- a/erpnext/regional/report/irs_1099/irs_1099.py +++ b/erpnext/regional/report/irs_1099/irs_1099.py @@ -1,29 +1,34 @@ # Copyright (c) 2018, 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 _, _dict -from frappe.utils import nowdate -from frappe.utils.data import fmt_money -from erpnext.accounts.utils import get_fiscal_year + from PyPDF2 import PdfFileWriter + +import frappe +from erpnext.accounts.utils import get_fiscal_year +from frappe import _ +from frappe.utils import cstr, nowdate +from frappe.utils.data import fmt_money +from frappe.utils.jinja import render_template from frappe.utils.pdf import get_pdf from frappe.utils.print_format import read_multi_pdf -from frappe.utils.jinja import render_template + +IRS_1099_FORMS_FILE_EXTENSION = ".pdf" def execute(filters=None): - filters = filters if isinstance(filters, _dict) else _dict(filters) - + filters = filters if isinstance(filters, frappe._dict) else frappe._dict(filters) if not filters: filters.setdefault('fiscal_year', get_fiscal_year(nowdate())[0]) filters.setdefault('company', frappe.db.get_default("company")) - region = frappe.db.get_value("Company", fieldname = ["country"], filters = { "name": filters.company }) + region = frappe.db.get_value("Company", + filters={"name": filters.company}, + fieldname=["country"]) + if region != 'United States': - return [],[] + return [], [] data = [] columns = get_columns() @@ -34,20 +39,23 @@ def execute(filters=None): s.tax_id as "tax_id", SUM(gl.debit_in_account_currency) AS "payments" FROM - `tabGL Entry` gl INNER JOIN `tabSupplier` s + `tabGL Entry` gl + INNER JOIN `tabSupplier` s WHERE s.name = gl.party - AND s.irs_1099 = 1 - AND gl.fiscal_year = %(fiscal_year)s - AND gl.party_type = "Supplier" - + AND s.irs_1099 = 1 + AND gl.fiscal_year = %(fiscal_year)s + AND gl.party_type = "Supplier" GROUP BY gl.party - ORDER BY - gl.party DESC""", {"fiscal_year": filters.fiscal_year, + gl.party DESC + """, { + "fiscal_year": filters.fiscal_year, "supplier_group": filters.supplier_group, - "company": filters.company}, as_dict=True) + "company": filters.company + }, as_dict=True) + return columns, data @@ -74,7 +82,6 @@ def get_columns(): "width": 120 }, { - "fieldname": "payments", "label": _("Total Payments"), "fieldtype": "Currency", @@ -88,23 +95,32 @@ def irs_1099_print(filters): if not filters: frappe._dict({ "company": frappe.db.get_default("Company"), - "fiscal_year": frappe.db.get_default("fiscal_year")}) + "fiscal_year": frappe.db.get_default("Fiscal Year") + }) else: filters = frappe._dict(json.loads(filters)) + + fiscal_year_doc = get_fiscal_year(fiscal_year=filters.fiscal_year, as_dict=True) + fiscal_year = cstr(fiscal_year_doc.year_start_date.year) + company_address = get_payer_address_html(filters.company) company_tin = frappe.db.get_value("Company", filters.company, "tax_id") + columns, data = execute(filters) template = frappe.get_doc("Print Format", "IRS 1099 Form").html output = PdfFileWriter() + for row in data: + row["fiscal_year"] = fiscal_year row["company"] = filters.company row["company_tin"] = company_tin row["payer_street_address"] = company_address - row["recipient_street_address"], row["recipient_city_state"] = get_street_address_html("Supplier", row.supplier) + row["recipient_street_address"], row["recipient_city_state"] = get_street_address_html( + "Supplier", row.supplier) row["payments"] = fmt_money(row["payments"], precision=0, currency="USD") - frappe._dict(row) pdf = get_pdf(render_template(template, row), output=output if output else None) - frappe.local.response.filename = filters.fiscal_year + " " + filters.company + " IRS 1099 Forms" + + frappe.local.response.filename = f"{filters.fiscal_year} {filters.company} IRS 1099 Forms{IRS_1099_FORMS_FILE_EXTENSION}" frappe.local.response.filecontent = read_multi_pdf(output) frappe.local.response.type = "download" @@ -120,36 +136,45 @@ def get_payer_address_html(company): ORDER BY address_type="Postal" DESC, address_type="Billing" DESC LIMIT 1 - """, {"company": company}, as_dict=True) + """, {"company": company}, as_dict=True) + + address_display = "" if address_list: company_address = address_list[0]["name"] - return frappe.get_doc("Address", company_address).get_display() - else: - return "" + address_display = frappe.get_doc("Address", company_address).get_display() + + return address_display def get_street_address_html(party_type, party): address_list = frappe.db.sql(""" SELECT link.parent - FROM `tabDynamic Link` link, `tabAddress` address - WHERE link.parenttype = "Address" - AND link.link_name = %(party)s - ORDER BY address.address_type="Postal" DESC, + FROM + `tabDynamic Link` link, + `tabAddress` address + WHERE + link.parenttype = "Address" + AND link.link_name = %(party)s + ORDER BY + address.address_type="Postal" DESC, address.address_type="Billing" DESC LIMIT 1 - """, {"party": party}, as_dict=True) + """, {"party": party}, as_dict=True) + + street_address = city_state = "" if address_list: supplier_address = address_list[0]["parent"] doc = frappe.get_doc("Address", supplier_address) + if doc.address_line2: - street = doc.address_line1 + "
    \n" + doc.address_line2 + "
    \n" + street_address = doc.address_line1 + "
    \n" + doc.address_line2 + "
    \n" else: - street = doc.address_line1 + "
    \n" - city = doc.city + ", " if doc.city else "" - city = city + doc.state + " " if doc.state else city - city = city + doc.pincode if doc.pincode else city - city += "
    \n" - return street, city - else: - return "", "" + street_address = doc.address_line1 + "
    \n" + + city_state = doc.city + ", " if doc.city else "" + city_state = city_state + doc.state + " " if doc.state else city_state + city_state = city_state + doc.pincode if doc.pincode else city_state + city_state += "
    \n" + + return street_address, city_state From c69ab6d184f2927bb8a148d2e3d20fc32ee0eba5 Mon Sep 17 00:00:00 2001 From: Afshan Date: Tue, 19 Jan 2021 19:29:31 +0530 Subject: [PATCH 506/728] fix: select sal comp while making sal struct --- erpnext/payroll/doctype/salary_structure/salary_structure.js | 4 ++++ erpnext/payroll/doctype/salary_structure/salary_structure.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.js b/erpnext/payroll/doctype/salary_structure/salary_structure.js index ba824c5d6f..6c7b382264 100755 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.js +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.js @@ -70,6 +70,9 @@ frappe.ui.form.on('Salary Structure', { }); }, + company: function(frm) { + frm.trigger('set_earning_deduction_component'); + }, currency: function(frm) { calculate_totals(frm.doc); @@ -117,6 +120,7 @@ frappe.ui.form.on('Salary Structure', { fields_read_only.forEach(function(field) { frappe.meta.get_docfield("Salary Detail", field, frm.doc.name).read_only = 1; }); + frm.trigger('set_earning_deduction_component'); }, assign_to_employees:function (frm) { diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py index 77914bb531..340f4e8569 100644 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py @@ -216,7 +216,7 @@ def get_earning_deduction_components(doctype, txt, searchfield, start, page_len, return frappe.db.sql(""" select t1.salary_component from `tabSalary Component` t1, `tabSalary Component Account` t2 - where t1.salary_component = t2.parent + where t1.name = t2.parent and t1.type = %s and t2.company = %s order by salary_component From 2e67a41c27b4878b9e2f078a40c7f2bb51c8e60c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 20 Jan 2021 10:12:01 +0530 Subject: [PATCH 507/728] fix: Update birthday avatar code --- .../templates/emails/birthday_reminder.html | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/erpnext/templates/emails/birthday_reminder.html b/erpnext/templates/emails/birthday_reminder.html index e4c91c722f..fd03637a53 100644 --- a/erpnext/templates/emails/birthday_reminder.html +++ b/erpnext/templates/emails/birthday_reminder.html @@ -1,12 +1,21 @@
    {% for person in birthday_persons %} - - - {% if not person.image %} {{ person.name[:1] }} {% endif %} + {% if person.image %} + - + {% else %} + + {{ person.name[:1] }} + + {% endif %} {% endfor %}
    From e2ed2324c3eb50f6bb5a67e95f218db6153f8a5d Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 20 Jan 2021 13:17:42 +0530 Subject: [PATCH 508/728] fix: (e-invoicing) qrcode image generation (#24395) * fix: invalid taxable value of item for e-invoice * fix: qrcode image duplicate error * fix: net total discount calculation * fix: auto fetch auth token --- erpnext/regional/india/e_invoice/utils.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index d0cac90e4d..83635a199e 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -161,9 +161,9 @@ def get_item_list(invoice): item.qty = abs(item.qty) item.discount_amount = abs(item.discount_amount * item.qty) - item.unit_rate = abs(item.base_amount / item.qty) - item.gross_amount = abs(item.base_amount) - item.taxable_value = abs(item.base_amount) + item.unit_rate = abs(item.base_net_amount / item.qty) + item.gross_amount = abs(item.base_net_amount) + item.taxable_value = abs(item.base_net_amount) item.batch_expiry_date = frappe.db.get_value('Batch', d.batch_no, 'expiry_date') if d.batch_no else None item.batch_expiry_date = format_date(item.batch_expiry_date, 'dd/mm/yyyy') if item.batch_expiry_date else None @@ -198,7 +198,7 @@ def update_item_taxes(invoice, item): if t.account_head in gst_accounts_list: item_tax_rate = item_tax_detail[0] # item tax amount excluding discount amount - item_tax_amount = (item_tax_rate / 100) * item.base_amount + item_tax_amount = (item_tax_rate / 100) * item.base_net_amount if t.account_head in gst_accounts.cess_account: item_tax_amount_after_discount = item_tax_detail[1] @@ -217,7 +217,10 @@ def update_item_taxes(invoice, item): def get_invoice_value_details(invoice): invoice_value_details = frappe._dict(dict()) - invoice_value_details.base_total = abs(invoice.base_total) + if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount: + invoice_value_details.base_total = abs(invoice.base_total) + else: + invoice_value_details.base_total = abs(invoice.base_net_total) invoice_value_details.invoice_discount_amt = invoice.base_discount_amount invoice_value_details.round_off = invoice.base_rounding_adjustment invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(invoice.base_grand_total) @@ -473,7 +476,7 @@ class GSPConnector(): "data": json.dumps(data, indent=4) if isinstance(data, dict) else data, "response": json.dumps(res, indent=4) if res else None }) - request_log.insert(ignore_permissions=True) + request_log.save(ignore_permissions=True) frappe.db.commit() def fetch_auth_token(self): @@ -486,7 +489,8 @@ class GSPConnector(): res = self.make_request('post', self.authenticate_url, headers) self.e_invoice_settings.auth_token = "{} {}".format(res.get('token_type'), res.get('access_token')) self.e_invoice_settings.token_expiry = add_to_date(None, seconds=res.get('expires_in')) - self.e_invoice_settings.save() + self.e_invoice_settings.save(ignore_permissions=True) + self.e_invoice_settings.reload() except Exception: self.log_error(res) @@ -757,7 +761,7 @@ class GSPConnector(): 'label': _('IRN Generated') } self.update_invoice() - + def attach_qrcode_image(self): qrcode = self.invoice.signed_qr_code doctype = self.invoice.doctype @@ -768,7 +772,7 @@ class GSPConnector(): 'file_name': 'QRCode_{}.png'.format(docname.replace('/', '-')), 'attached_to_doctype': doctype, 'attached_to_name': docname, - 'content': 'qrcode', + 'content': str(base64.b64encode(os.urandom(64))), 'is_private': 1 }) _file.insert() From 912647f4f23f2b6a6a7c10290682e757fdf840f4 Mon Sep 17 00:00:00 2001 From: Afshan Date: Wed, 20 Jan 2021 13:58:32 +0530 Subject: [PATCH 509/728] fix: allow statistical component in salary structure. --- .../payroll/doctype/salary_structure/salary_structure.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py index 340f4e8569..bf1c746964 100644 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py @@ -216,8 +216,9 @@ def get_earning_deduction_components(doctype, txt, searchfield, start, page_len, return frappe.db.sql(""" select t1.salary_component from `tabSalary Component` t1, `tabSalary Component Account` t2 - where t1.name = t2.parent + where (t1.name = t2.parent and t1.type = %s - and t2.company = %s + and t2.company = %s) + or (t1.statistical_component = 1) order by salary_component - """, (filters['type'], filters['company']) ) + """, (filters['type'], filters['company'])) From 36e3e05a0edcbe2736df77a9c57c7eae63f25f30 Mon Sep 17 00:00:00 2001 From: Afshan Date: Wed, 20 Jan 2021 16:48:42 +0530 Subject: [PATCH 510/728] fix: query for salary componet in salary structure --- .../doctype/salary_structure/salary_structure.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py index bf1c746964..e71803172c 100644 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py @@ -217,8 +217,12 @@ def get_earning_deduction_components(doctype, txt, searchfield, start, page_len, select t1.salary_component from `tabSalary Component` t1, `tabSalary Component Account` t2 where (t1.name = t2.parent - and t1.type = %s - and t2.company = %s) - or (t1.statistical_component = 1) + and t1.type = %(type)s + and t2.company = %(company)s) + or (t1.type = %(type)s + and t1.statistical_component = 1) order by salary_component - """, (filters['type'], filters['company'])) + """,{ + "type": filters['type'], + "company": filters['company'] + }) From 599322871a55d83be572dc12b631807308d9d631 Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 20 Jan 2021 17:44:08 +0530 Subject: [PATCH 511/728] fix: product query issues --- erpnext/setup/doctype/item_group/item_group.py | 4 +++- erpnext/shopping_cart/product_query.py | 11 +++++++++-- erpnext/www/all-products/index.py | 4 +++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index f545b989d3..bff806d547 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -77,10 +77,12 @@ class ItemGroup(NestedSet, WebsiteGenerator): search = frappe.form_dict.search field_filters = frappe.parse_json(frappe.form_dict.field_filters) attribute_filters = frappe.parse_json(frappe.form_dict.attribute_filters) + start = frappe.parse_json(frappe.form_dict.start) else: search = None attribute_filters = None field_filters = {} + start = 0 if not field_filters: field_filters = {} @@ -89,7 +91,7 @@ class ItemGroup(NestedSet, WebsiteGenerator): field_filters['item_group'] = self.name engine = ProductQuery() - context.items = engine.query(attribute_filters, field_filters, search) + context.items = engine.query(attribute_filters, field_filters, search, start) filter_engine = ProductFiltersBuilder(self.name) diff --git a/erpnext/shopping_cart/product_query.py b/erpnext/shopping_cart/product_query.py index c5d34459b1..da9e798327 100644 --- a/erpnext/shopping_cart/product_query.py +++ b/erpnext/shopping_cart/product_query.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt import frappe +from erpnext.shopping_cart.product_info import get_product_info_for_website class ProductQuery: """Query engine for product listing @@ -21,7 +22,7 @@ class ProductQuery: self.settings = frappe.get_doc("Products Settings") self.cart_settings = frappe.get_doc("Shopping Cart Settings") self.page_length = self.settings.products_per_page or 20 - self.fields = ['name', 'item_name', 'website_image', 'variant_of', 'has_variants', 'item_group', 'image', 'web_long_description', 'description', 'route'] + self.fields = ['name', 'item_name', 'item_code', 'website_image', 'variant_of', 'has_variants', 'item_group', 'image', 'web_long_description', 'description', 'route'] self.filters = [['show_in_website', '=', 1]] self.or_filters = [] @@ -57,6 +58,8 @@ class ProductQuery: ["Item Variant Attribute", "attribute_value", "in", values], ], or_filters=self.or_filters, + start=start, + limit=self.page_length ) items_dict = {item.name: item for item in items} @@ -66,7 +69,11 @@ class ProductQuery: result = [items_dict.get(item) for item in list(set.intersection(*all_items))] else: - result = frappe.get_all("Item", fields=self.fields, filters=self.filters, or_filters=self.or_filters, limit=self.page_length) + result = frappe.get_all("Item", fields=self.fields, filters=self.filters, or_filters=self.or_filters, start=start, limit=self.page_length) + + for item in result: + product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get('product_info') + item.formatted_price = product_info['price'].get('formatted_price') if product_info['price'] else None return result diff --git a/erpnext/www/all-products/index.py b/erpnext/www/all-products/index.py index 2d4a357e5e..2271b4c23c 100644 --- a/erpnext/www/all-products/index.py +++ b/erpnext/www/all-products/index.py @@ -12,11 +12,13 @@ def get_context(context): search = frappe.form_dict.search field_filters = frappe.parse_json(frappe.form_dict.field_filters) attribute_filters = frappe.parse_json(frappe.form_dict.attribute_filters) + start = frappe.parse_json(frappe.form_dict.start) else: search = field_filters = attribute_filters = None + start = 0 engine = ProductQuery() - context.items = engine.query(attribute_filters, field_filters, search) + context.items = engine.query(attribute_filters, field_filters, search, start) product_settings = get_product_settings() filter_engine = ProductFiltersBuilder() From ac7bc78afeec9b9cf77cf58d90b88fb013280bae Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 20 Jan 2021 17:46:19 +0530 Subject: [PATCH 512/728] fix: show product price in item card --- erpnext/portal/product_configurator/utils.py | 7 +++++-- .../web_template/item_card_group/item_card_group.html | 2 +- erpnext/www/all-products/item_row.html | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 9ba4cdc514..4693d44509 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -1,6 +1,7 @@ import frappe from frappe.utils import cint from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager +from erpnext.shopping_cart.product_info import get_product_info_for_website def get_field_filter_data(): product_settings = get_product_settings() @@ -356,10 +357,10 @@ def get_items(filters=None, search=None): results = frappe.db.sql(''' SELECT - `tabItem`.`name`, `tabItem`.`item_name`, + `tabItem`.`name`, `tabItem`.`item_name`, `tabItem`.`item_code`, `tabItem`.`website_image`, `tabItem`.`image`, `tabItem`.`web_long_description`, `tabItem`.`description`, - `tabItem`.`route` + `tabItem`.`route`, `tabItem`.`item_group` FROM `tabItem` {left_join} @@ -384,6 +385,8 @@ def get_items(filters=None, search=None): for r in results: r.description = r.web_long_description or r.description r.image = r.website_image or r.image + product_info = get_product_info_for_website(r.item_code, skip_quotation_creation=True).get('product_info') + r.formatted_price = product_info['price'].get('formatted_price') if product_info['price'] else None return results diff --git a/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html index d39d1b7b50..890ae502c8 100644 --- a/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html +++ b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html @@ -26,7 +26,7 @@ {%- set item = frappe.get_doc("Item", item) -%} {{ item_card( item.item_name, item.image, item.route, item.description, - item.standard_rate, item.item_group, values['card_' + index + '_featured'], + None, item.item_group, values['card_' + index + '_featured'], True, "Center" ) }} {%- endif -%} diff --git a/erpnext/www/all-products/item_row.html b/erpnext/www/all-products/item_row.html index b5280eacb7..20fc9a4878 100644 --- a/erpnext/www/all-products/item_row.html +++ b/erpnext/www/all-products/item_row.html @@ -2,6 +2,6 @@ {{ item_card( item.item_name or item.name, item.website_image or item.image, item.route, item.website_description or item.description, - item.standard_rate, item.item_group + item.formatted_price, item.item_group ) }} From 706eae7cacc0fb0f7bd922e69c618acc2047200d Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 20 Jan 2021 17:47:25 +0530 Subject: [PATCH 513/728] fix: product page fixes --- erpnext/templates/generators/item/item.html | 3 + .../generators/item/item_add_to_cart.html | 9 ++- .../generators/item/item_configure.html | 4 +- .../generators/item/item_configure.js | 67 +++++++++++-------- .../generators/item/item_details.html | 5 +- 5 files changed, 52 insertions(+), 36 deletions(-) diff --git a/erpnext/templates/generators/item/item.html b/erpnext/templates/generators/item/item.html index 889908031e..135982d709 100644 --- a/erpnext/templates/generators/item/item.html +++ b/erpnext/templates/generators/item/item.html @@ -3,6 +3,9 @@ {% block title %} {{ title }} {% endblock %} {% block breadcrumbs %} +
    + {% include "templates/includes/breadcrumbs.html" %} +
    {% endblock %} {% block page_content %} diff --git a/erpnext/templates/generators/item/item_add_to_cart.html b/erpnext/templates/generators/item/item_add_to_cart.html index 3210e2497f..f5adbf01e3 100644 --- a/erpnext/templates/generators/item/item_add_to_cart.html +++ b/erpnext/templates/generators/item/item_add_to_cart.html @@ -30,7 +30,7 @@ {% endif %}
    {% endif %} -
    +
    {% if product_info.price and (cart_settings.allow_items_not_in_stock or product_info.in_stock) %} {% endif %} diff --git a/erpnext/templates/generators/item/item_configure.html b/erpnext/templates/generators/item/item_configure.html index 73f9ec99b3..b61ac73072 100644 --- a/erpnext/templates/generators/item/item_configure.html +++ b/erpnext/templates/generators/item/item_configure.html @@ -1,9 +1,9 @@ {% if shopping_cart and shopping_cart.cart_settings.enabled %} {% set cart_settings = shopping_cart.cart_settings %} -
    +
    {% if cart_settings.enable_variants | int %} - + ` : ''; const items_found = filtered_items_count === 1 ? __('{0} item found.', [filtered_items_count]) : __('{0} items found.', [filtered_items_count]); - const item_found_status = ` - ` + : ``; return ` - ${item_add_to_cart} ${item_found_status} + ${item_add_to_cart} `; } @@ -254,8 +265,8 @@ class ItemConfigure { } append_status_area() { - this.dialog.$status_area = $('
    '); - this.dialog.$wrapper.find('.modal-body').prepend(this.dialog.$status_area); + this.dialog.$status_area = $('
    '); + this.dialog.$wrapper.find('.modal-body').append(this.dialog.$status_area); this.dialog.$wrapper.on('click', '[data-action]', (e) => { e.preventDefault(); const $target = $(e.currentTarget); @@ -263,7 +274,7 @@ class ItemConfigure { const method = this[action]; method.call(this, e); }); - this.dialog.$body.css({ maxHeight: '75vh', overflow: 'auto', overflowX: 'hidden' }); + this.dialog.$wrapper.addClass('item-configurator-dialog'); } get_next_attribute_and_values(selected_attributes) { diff --git a/erpnext/templates/generators/item/item_details.html b/erpnext/templates/generators/item/item_details.html index 6a85ca50e6..3b77585827 100644 --- a/erpnext/templates/generators/item/item_details.html +++ b/erpnext/templates/generators/item/item_details.html @@ -1,8 +1,5 @@ -
    +
    -
    - {% include "templates/includes/breadcrumbs.html" %} -

    {{ item_name }}

    From b00eb1b0cc2ccf7e31432eb1724e96af27c3ed93 Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 20 Jan 2021 17:52:54 +0530 Subject: [PATCH 514/728] feat: shopping cart page --- .../images/ui-states/cart-empty-state.png | Bin 0 -> 1593 bytes erpnext/public/scss/shopping_cart.scss | 268 ++++++++++++++++-- erpnext/shopping_cart/cart.py | 36 ++- erpnext/templates/includes/cart.js | 89 ++++-- .../templates/includes/cart/address_card.html | 19 +- .../includes/cart/address_picker_card.html | 12 + .../templates/includes/cart/cart_address.html | 56 ++-- .../includes/cart/cart_address_picker.html | 4 + .../templates/includes/cart/cart_items.html | 16 +- erpnext/templates/includes/macros.html | 4 + .../includes/navbar/navbar_items.html | 8 +- .../templates/includes/order/order_taxes.html | 79 +++--- erpnext/templates/pages/cart.html | 173 ++++++----- erpnext/www/all-products/index.html | 10 +- 14 files changed, 573 insertions(+), 201 deletions(-) create mode 100644 erpnext/public/images/ui-states/cart-empty-state.png create mode 100644 erpnext/templates/includes/cart/address_picker_card.html create mode 100644 erpnext/templates/includes/cart/cart_address_picker.html diff --git a/erpnext/public/images/ui-states/cart-empty-state.png b/erpnext/public/images/ui-states/cart-empty-state.png new file mode 100644 index 0000000000000000000000000000000000000000..e1ead0e175d204e267a9010e70f54ff98d08250c GIT binary patch literal 1593 zcmV-92FCe`P)Fyc*d{ru;znM4Ry#8+h7A#n>pu7kukWQzw`U#dBvLe6H zTCZa;tN7??A$t1ydjH4f(@Q7`g@__SUzS#Ol~Q{o2eb|_lJ{Eaz7X=E7Em+q3(+l$ zFt4KG1YstTh#>sEpE|!)@u?xC+{EW<8H9Nel~hoaG6{1kDyyIlr2F~iWFv5 zlq*3|iV$W+)R-VdE7ionyWkL?!g7g<%^;!w7GMIu#0%)EtE*GjMZ<@%OrjKKbjMe0AyfgJUNn24|*3VY$|T#@ruA*HYp zQ6x!9eU5MThy9=cJTM|mhya6b`si(^5jG;~|wDchS*)~Jp$?_-uxk)(_RkB|_2v@wn~4eAtcB1yZz8`>F4bON>b za%pK(55}z3FE~|A@Bg1sD1%zL?Z!qk{KKM}@+gsBKHb2!&2eO0mq-Y)iu3pV?PuTD z<)j0IGOUh2zAQAuHlH>)!|<8vvGdoz{@#KdkRUq7XZhg598+uRrqnTE>R7_w;p=t! z-o%WGVtUs?w8Fy?d=7lKl6l9GNjlZTMx}*`B2tSA9i)bVHhfR1#Kcj>8`nuXjvN;R zXIiVHxIUv5QQ<(NQz``st4S#juo9-CGA4}T#P!2nrRnCH4C)Ru=<<$Z+7lHHBwX1R zwPF%&e3a5hDCi-znkca^Kx6%;50k_d7B#UlPfE11_0^om#95MDDy2BU%y~>1 zc3*v5+uq#R%X0IUz%s)i8gw^IhG|K{8Ys+5hMC5uQfc9hs!;}kQb+gU^C5K_AqUn) zF}{V32-2Da?b{HYmnn zbbzh6WcJeF=qq+1J z4M}bjM$JC3P!;HGG znb$Cx`%xr?gkj)K$}lkyc0mFmBO_BhI5$QEa5I@W$RIONEkY8JyTj>H7?`1m{_(Ev5+<&%>o8@c z5>?TZq?@z6|C~Sgy)^o`l_q!m-pl zywJyxfq4)X&ZsV}x4-VZ$s~)IWnnj>%%D?<0N=^IYBDRr%rY0oZ~hK{Ks()NV~C*Qi*y%H9N$3Ckl6+p?Ly$Q5vJ6Ks(`(r!qZX<5@im$oqyvzJ)h;H5xxM;D} z+&JXVCyHW<5avTv%=Vwy%^QhDF+PR46qQsElUfwxLzoLuX$3KvMKLag*%Xyk5R+RJ z<3gAXQF#S1)R|CCZl|#=9sciLf|Pz6CLbiDK>&78X^MAf|9pOjMW { + const type = $(e.currentTarget).parents('.address-container').attr('data-address-type'); + $(d.get_field('address_picker').wrapper).html( + this.get_address_template(type) + ); + d.show(); }); }, + get_update_address_dialog() { + return new frappe.ui.Dialog({ + title: "Select Address", + fields: [{ + 'fieldtype': 'HTML', + 'fieldname': 'address_picker', + }], + primary_action_label: __('Set Address'), + primary_action: () => { + const $card = d.$wrapper.find('.address-card.active'); + const address_type = $card.closest('[data-address-type]').attr('data-address-type'); + const address_name = $card.closest('[data-address-name]').attr('data-address-name'); + frappe.call({ + type: "POST", + method: "erpnext.shopping_cart.cart.update_cart_address", + freeze: true, + args: { + address_type, + address_name + }, + callback: function(r) { + d.hide(); + if(!r.exc) { + $(".cart-tax-items").html(r.message.taxes); + shopping_cart.parent.find( + `.address-container[data-address-type="${address_type}"]` + ).html(r.message.address); + } + } + }); + } + }); + }, + + get_address_template(type) { + return { + shipping: `
    +
    + {% for address in shipping_addresses %} +
    + {% include "templates/includes/cart/address_picker_card.html" %} +
    + {% endfor %} +
    +
    `, + billing: `
    +
    + {% for address in billing_addresses %} +
    + {% include "templates/includes/cart/address_picker_card.html" %} +
    + {% endfor %} +
    +
    `, + }[type]; + }, + bind_place_order: function() { $(".btn-place-order").on("click", function() { shopping_cart.place_order(this); @@ -221,6 +269,7 @@ $.extend(shopping_cart, { frappe.ready(function() { $(".cart-icon").hide(); + shopping_cart.parent = $(".cart-container"); shopping_cart.bind_events(); }); diff --git a/erpnext/templates/includes/cart/address_card.html b/erpnext/templates/includes/cart/address_card.html index 646210e65f..667144bd87 100644 --- a/erpnext/templates/includes/cart/address_card.html +++ b/erpnext/templates/includes/cart/address_card.html @@ -1,12 +1,17 @@
    -
    - +
    + {{ _('Change') }}
    -
    -
    {{ address.title }}
    -

    +

    +
    {{ address.title }}
    +
    {{ address.display }} -

    - {{ _('Edit') }} +
    + + + + + {{ _('Edit') }} +
    diff --git a/erpnext/templates/includes/cart/address_picker_card.html b/erpnext/templates/includes/cart/address_picker_card.html new file mode 100644 index 0000000000..2334ea2955 --- /dev/null +++ b/erpnext/templates/includes/cart/address_picker_card.html @@ -0,0 +1,12 @@ +
    +
    + +
    +
    +
    {{ address.title }}
    +

    + {{ address.display }} +

    + {{ _('Edit') }} +
    +
    \ No newline at end of file diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html index aa25c885fe..3c8267c43d 100644 --- a/erpnext/templates/includes/cart/cart_address.html +++ b/erpnext/templates/includes/cart/cart_address.html @@ -14,31 +14,39 @@
    {% endif %} -
    -
    {{ _("Shipping Address") }}
    +
    +
    {{ _("Shipping Address") }}
    +
    + {% for address in shipping_addresses %} + {% if doc.shipping_address_name == address.name %}
    - {% for address in shipping_addresses %} -
    - {% include "templates/includes/cart/address_card.html" %} -
    - {% endfor %} +
    + {% include "templates/includes/cart/address_card.html" %} +
    + {% endif %} + {% endfor %}
    -
    -
    {{ _("Billing Address") }}
    -
    - {% for address in billing_addresses %} -
    - {% include "templates/includes/cart/address_card.html" %} -
    - {% endfor %} -
    +
    +
    -
    - - +
    +
    {{ _("Billing Address") }}
    +
    + {% for address in billing_addresses %} + {% if doc.customer_address == address.name %} +
    +
    + {% include "templates/includes/cart/address_card.html" %} +
    +
    + {% endif %} + {% endfor %}
    - + diff --git a/erpnext/templates/includes/cart/cart_address_picker.html b/erpnext/templates/includes/cart/cart_address_picker.html new file mode 100644 index 0000000000..72cc5f5142 --- /dev/null +++ b/erpnext/templates/includes/cart/cart_address_picker.html @@ -0,0 +1,4 @@ +
    +
    {{ _("Shipping Address") }}
    +
    + diff --git a/erpnext/templates/includes/cart/cart_items.html b/erpnext/templates/includes/cart/cart_items.html index ca5744bebb..75441c42bc 100644 --- a/erpnext/templates/includes/cart/cart_items.html +++ b/erpnext/templates/includes/cart/cart_items.html @@ -1,15 +1,15 @@ {% for d in doc.items %} -
    +
    {{ d.item_name }}
    -
    +
    {{ d.item_code }}
    {%- set variant_of = frappe.db.get_value('Item', d.item_code, 'variant_of') %} {% if variant_of %} - + {{ _('Variant of') }} {{ variant_of }} {% endif %} @@ -20,20 +20,20 @@
    - + - + - +
    {% if cart_settings.enable_checkout %} - +
    {{ d.get_formatted('amount') }}
    - + {{ _('Rate:') }} {{ d.get_formatted('rate') }} diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html index 74c84ba27c..0adfbc9dfd 100644 --- a/erpnext/templates/includes/macros.html +++ b/erpnext/templates/includes/macros.html @@ -90,6 +90,10 @@
    {% if image %} {{ title }} + {% else %} +
    + {{ frappe.utils.get_abbr(title) }} +
    {% endif %} {{ item_card_body(title, description, url, rate, category, is_featured, align) }}
    diff --git a/erpnext/templates/includes/navbar/navbar_items.html b/erpnext/templates/includes/navbar/navbar_items.html index 4daf0e74e0..133d99e5eb 100644 --- a/erpnext/templates/includes/navbar/navbar_items.html +++ b/erpnext/templates/includes/navbar/navbar_items.html @@ -2,9 +2,11 @@ {% block navbar_right_extension %} {% endblock %} \ No newline at end of file diff --git a/erpnext/templates/includes/order/order_taxes.html b/erpnext/templates/includes/order/order_taxes.html index ebec838d03..d2c458e0a4 100644 --- a/erpnext/templates/includes/order/order_taxes.html +++ b/erpnext/templates/includes/order/order_taxes.html @@ -29,12 +29,12 @@ {{ _("Discount") }} - {% set tot_quotation_discount = [] %} - {%- for item in doc.items -%} - {% if tot_quotation_discount.append((((item.price_list_rate * item.qty) - * item.discount_percentage) / 100)) %}{% endif %} - {% endfor %} - {{ frappe.utils.fmt_money((tot_quotation_discount | sum),currency=doc.currency) }} + {% set tot_quotation_discount = [] %} + {%- for item in doc.items -%} + {% if tot_quotation_discount.append((((item.price_list_rate * item.qty) + * item.discount_percentage) / 100)) %}{% endif %} + {% endfor %} + {{ frappe.utils.fmt_money((tot_quotation_discount | sum),currency=doc.currency) }} {% endif %} @@ -47,51 +47,52 @@ {{ _("Total Amount") }} - - {% set total_amount = [] %} - {%- for item in doc.items -%} - {% if total_amount.append((item.price_list_rate * item.qty)) %}{% endif %} - {% endfor %} - {{ frappe.utils.fmt_money((total_amount | sum),currency=doc.currency) }} - + + {% set total_amount = [] %} + {%- for item in doc.items -%} + {% if total_amount.append((item.price_list_rate * item.qty)) %}{% endif %} + {% endfor %} + {{ frappe.utils.fmt_money((total_amount | sum),currency=doc.currency) }} + - - {{ _("Applied Coupon Code") }} - - - - {%- for row in frappe.get_all(doctype="Coupon Code", - fields=["coupon_code"], filters={ "name":doc.coupon_code}) -%} - {{ row.coupon_code }} - {% endfor %} - - + + {{ _("Applied Coupon Code") }} + + + + {%- for row in frappe.get_all(doctype="Coupon Code", + fields=["coupon_code"], filters={ "name":doc.coupon_code}) -%} + {{ row.coupon_code }} + {% endfor %} + + - - {{ _("Discount") }} - - - - {% set tot_SO_discount = [] %} - {%- for item in doc.items -%} - {% if tot_SO_discount.append((((item.price_list_rate * item.qty) - * item.discount_percentage) / 100)) %}{% endif %} - {% endfor %} - {{ frappe.utils.fmt_money((tot_SO_discount | sum),currency=doc.currency) }} - - + + {{ _("Discount") }} + + + + {% set tot_SO_discount = [] %} + {%- for item in doc.items -%} + {% if tot_SO_discount.append((((item.price_list_rate * item.qty) + * item.discount_percentage) / 100)) %}{% endif %} + {% endfor %} + {{ frappe.utils.fmt_money((tot_SO_discount | sum),currency=doc.currency) }} + + {% endif %} {% endif %} - + + {{ _("Grand Total") }} - + {{ doc.get_formatted("grand_total") }} diff --git a/erpnext/templates/pages/cart.html b/erpnext/templates/pages/cart.html index 3033d1587d..2cabf5a9e0 100644 --- a/erpnext/templates/pages/cart.html +++ b/erpnext/templates/pages/cart.html @@ -2,7 +2,7 @@ {% block title %} {{ _("Shopping Cart") }} {% endblock %} -{% block header %}

    {{ _("Shopping Cart") }}

    {% endblock %} +{% block header %}

    {{ _("Shopping Cart") }}

    {% endblock %} -
    -
    -
    + + + + + + + {% for d in doc.items %} -
    -
    +
    + + + + {% endfor %} - + +
    {{ _("Item") }} - -
    +
    {{ _("Quantity") }} - -
    +
    {{ _("Amount") }} - - +
    {{ item_name_and_description(d) }} - -
    +
    {{ d.qty }} {% if d.delivered_qty is defined and d.delivered_qty != None %}

    {{ _("Delivered") }} {{ d.delivered_qty }}

    {% endif %} - -
    +
    {{ d.get_formatted("amount") }}

    {{ _("Rate:") }} {{ d.get_formatted("rate") }}

    - - +
    From ff12f914867e22734f5cf2d9a280b43401988d06 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 27 Jan 2021 19:17:38 +0530 Subject: [PATCH 571/728] feat: charging tcs on sales invoice --- .../doctype/sales_invoice/sales_invoice.py | 27 ++++ .../tax_withholding_category.py | 92 +++++++++++--- .../test_tax_withholding_category.py | 117 +++++++++++++++++- 3 files changed, 215 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 566734e7d1..0be63a8c31 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -21,6 +21,7 @@ from erpnext.accounts.general_ledger import get_round_off_account_and_cost_cente from erpnext.accounts.doctype.loyalty_program.loyalty_program import \ get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points from erpnext.accounts.deferred_revenue import validate_service_stop_date +from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details from erpnext.healthcare.utils import manage_invoice_submit_cancel @@ -73,6 +74,8 @@ class SalesInvoice(SellingController): if not self.is_pos: self.so_dn_required() + + self.set_tax_withholding() self.validate_proj_cust() self.validate_pos_return() @@ -151,6 +154,30 @@ class SalesInvoice(SellingController): if cost_center_company != self.company: frappe.throw(_("Row #{0}: Cost Center {1} does not belong to company {2}").format(frappe.bold(item.idx), frappe.bold(item.cost_center), frappe.bold(self.company))) + def set_tax_withholding(self): + tax_withholding_details = get_party_tax_withholding_details(self) + + if not tax_withholding_details: + return + + accounts = [] + for d in self.taxes: + if d.account_head == tax_withholding_details.get("account_head"): + d.update(tax_withholding_details) + accounts.append(d.account_head) + + if not accounts or tax_withholding_details.get("account_head") not in accounts: + self.append("taxes", tax_withholding_details) + + to_remove = [d for d in self.taxes + if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")] + + for d in to_remove: + self.remove(d) + + # calculate totals again after applying TDS + self.calculate_taxes_and_totals() + def before_save(self): set_account_for_mode_of_payment(self) 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 3e0ba9ac6a..36ed6decad 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -104,31 +104,30 @@ def get_lower_deduction_certificate(fiscal_year, pan_no): def get_tax_amount(party_type, parties, ref_doc, tax_details, fiscal_year_details, pan_no=None): fiscal_year = fiscal_year_details[0] - vouchers = get_invoice_vouchers(parties, fiscal_year, ref_doc.company, party_type=party_type) or [""] + vouchers = get_invoice_vouchers(parties, fiscal_year, ref_doc.company, party_type=party_type) advance_vouchers = get_advance_vouchers(parties, fiscal_year, ref_doc.company, party_type=party_type) - tax_vouchers = vouchers + advance_vouchers + taxable_vouchers = vouchers + advance_vouchers tax_deducted = 0 - dr_or_cr = 'credit' if party_type == 'Supplier' else 'debit' - if tax_vouchers: + if taxable_vouchers: + # check if tds / tcs is already charged on taxable vouchers filters = { - dr_or_cr: ['>', 0], - 'account': tax_details.account_head, + 'is_cancelled': 0, + 'credit': ['>', 0], 'fiscal_year': fiscal_year, - 'voucher_no': ['in', tax_vouchers], - 'is_cancelled': 0 + 'account': tax_details.account_head, + 'voucher_no': ['in', taxable_vouchers], } - field = "sum({})".format(dr_or_cr) + field = "sum(credit)" tax_deducted = frappe.db.get_value('GL Entry', filters, field) or 0.0 tax_amount = 0 + posting_date = ref_doc.posting_date if party_type == 'Supplier': - net_total = ref_doc.net_total - posting_date = ref_doc.posting_date ldc = get_lower_deduction_certificate(fiscal_year, pan_no) - if tax_deducted: + net_total = ref_doc.net_total if ldc: tax_amount = get_tds_amount_from_ldc(ldc, parties, fiscal_year, pan_no, tax_details, posting_date, net_total) else: @@ -139,6 +138,19 @@ def get_tax_amount(party_type, parties, ref_doc, tax_details, fiscal_year_detail fiscal_year_details, vouchers ) + elif party_type == 'Customer': + if tax_deducted: + grand_total = get_invoice_total_without_tcs(ref_doc, tax_details) + # if already tcs is charged, then (net total + gst amount) of invoice is chargeable + tax_amount = grand_total * tax_details.rate / 100 if grand_total > 0 else 0 + else: + # if no tcs has been charged in FY, + # then (prev invoices + advances) value crossing the threshold are chargeable + tax_amount = get_tcs_amount( + parties, ref_doc, tax_details, + fiscal_year_details, vouchers, advance_vouchers + ) + return tax_amount def get_invoice_vouchers(parties, fiscal_year, company, party_type='Supplier'): @@ -154,7 +166,7 @@ def get_invoice_vouchers(parties, fiscal_year, company, party_type='Supplier'): 'is_cancelled': 0 } - return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck="voucher_no") + return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck="voucher_no") or [""] def get_advance_vouchers(parties, fiscal_year=None, company=None, from_date=None, to_date=None, party_type='Supplier'): # for advance vouchers, debit and credit is reversed @@ -162,10 +174,11 @@ def get_advance_vouchers(parties, fiscal_year=None, company=None, from_date=None filters = { dr_or_cr: ['>', 0], + 'is_opening': 'No', + 'is_cancelled': 0, 'party_type': party_type, 'party': ['in', parties], - 'is_opening': 'No', - 'is_cancelled': 0 + 'against_voucher': ['is', 'not set'] } if fiscal_year: @@ -175,7 +188,7 @@ def get_advance_vouchers(parties, fiscal_year=None, company=None, from_date=None if from_date and to_date: filters['posting_date'] = ['between', (from_date, to_date)] - return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck='voucher_no') + return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck='voucher_no') or [""] def get_tds_amount(ldc, parties, ref_doc, tax_details, fiscal_year_details, vouchers): tds_amount = 0 @@ -210,6 +223,53 @@ def get_tds_amount(ldc, parties, ref_doc, tax_details, fiscal_year_details, vouc return tds_amount +def get_tcs_amount(parties, ref_doc, tax_details, fiscal_year_details, vouchers, adv_vouchers): + tcs_amount = 0 + fiscal_year, _, _ = fiscal_year_details + + # sum of debit entries made from sales invoices + invoiced_amt = frappe.db.get_value('GL Entry', { + 'is_cancelled': 0, + 'party': ['in', parties], + 'company': ref_doc.company, + 'voucher_no': ['in', vouchers], + }, 'sum(debit)') or 0.0 + + # sum of credit entries made from PE / JV with unset 'against voucher' + advance_amt = frappe.db.get_value('GL Entry', { + 'is_cancelled': 0, + 'party': ['in', parties], + 'company': ref_doc.company, + 'voucher_no': ['in', adv_vouchers], + }, 'sum(credit)') or 0.0 + + # sum of credit entries made from sales invoice + credit_note_amt = frappe.db.get_value('GL Entry', { + 'is_cancelled': 0, + 'credit': ['>', 0], + 'party': ['in', parties], + 'fiscal_year': fiscal_year, + 'company': ref_doc.company, + 'voucher_type': 'Sales Invoice', + }, 'sum(credit)') or 0.0 + + current_invoice_total = get_invoice_total_without_tcs(ref_doc, tax_details) + chargeable_amt = current_invoice_total + invoiced_amt + advance_amt - credit_note_amt + + threshold = tax_details.get('threshold', 0) + cumulative_threshold = tax_details.get('cumulative_threshold', 0) + + if ((threshold and chargeable_amt >= threshold) or (cumulative_threshold and chargeable_amt >= cumulative_threshold)): + tcs_amount = chargeable_amt * tax_details.rate / 100 if chargeable_amt > 0 else 0 + + return tcs_amount + +def get_invoice_total_without_tcs(ref_doc, tax_details): + tcs_tax_row = [d for d in ref_doc.taxes if d.account_head == tax_details.account_head] + tcs_tax_row_amount = tcs_tax_row[0].base_tax_amount if tcs_tax_row else 0 + + return ref_doc.grand_total - tcs_tax_row_amount + def get_tds_amount_from_ldc(ldc, parties, fiscal_year, pan_no, tax_details, posting_date, net_total): tds_amount = 0 limit_consumed = frappe.db.get_value('Purchase Invoice', { 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 ef77674372..c8bd0834d8 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 @@ -9,7 +9,7 @@ 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"] +test_dependencies = ["Supplier Group", "Customer Group"] class TestTaxWithholdingCategory(unittest.TestCase): @classmethod @@ -128,9 +128,42 @@ class TestTaxWithholdingCategory(unittest.TestCase): for d in invoices: d.cancel() + def test_cumulative_threshold_tcs(self): + frappe.db.set_value("Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS") + invoices = [] + + # create invoices for lower than single threshold tax rate + for _ in range(2): + si = create_sales_invoice(customer = "Test TCS Customer") + si.submit() + invoices.append(si) + + # create another invoice whose total when added to previously created invoice, + # surpasses cumulative threshhold + si = create_sales_invoice(customer = "Test TCS Customer") + si.submit() + + # assert tax collection on total invoice amount created until now + tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC']) + self.assertEqual(tcs_charged, 3000) + self.assertEqual(si.grand_total, 13000) + invoices.append(si) + + # TCS is already collected once, so going forward system will collect TCS on every invoice + si = create_sales_invoice(customer = "Test TCS Customer", rate=5000) + si.submit() + + tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC']) + self.assertEqual(tcs_charged, 500) + invoices.append(si) + + #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'}) + item = frappe.db.get_value('Item', {'item_name': 'TDS Item'}, "name") args = frappe._dict(args) pi = frappe.get_doc({ @@ -145,7 +178,7 @@ def create_purchase_invoice(**args): "taxes": [], "items": [{ 'doctype': 'Purchase Invoice Item', - 'item_code': item.name, + 'item_code': item, 'qty': args.qty or 1, 'rate': args.rate or 10000, 'cost_center': 'Main - _TC', @@ -156,6 +189,33 @@ def create_purchase_invoice(**args): pi.save() return pi +def create_sales_invoice(**args): + # return sales invoice doc object + item = frappe.db.get_value('Item', {'item_name': 'TCS Item'}, "name") + + args = frappe._dict(args) + si = frappe.get_doc({ + "doctype": "Sales Invoice", + "posting_date": today(), + "customer": args.customer, + "company": '_Test Company', + "taxes_and_charges": "", + "currency": "INR", + "debit_to": "Debtors - _TC", + "taxes": [], + "items": [{ + 'doctype': 'Sales Invoice Item', + 'item_code': item, + 'qty': args.qty or 1, + 'rate': args.rate or 10000, + 'cost_center': 'Main - _TC', + 'expense_account': 'Cost of Goods Sold - _TC' + }] + }) + + si.save() + return si + def create_records(): # create a new suppliers for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']: @@ -168,7 +228,17 @@ def create_records(): "doctype": "Supplier", }).insert() - # create an item + for name in ['Test TCS Customer']: + if frappe.db.exists('Customer', name): + continue + + frappe.get_doc({ + "customer_group": "_Test Customer Group", + "customer_name": name, + "doctype": "Customer" + }).insert() + + # create item if not frappe.db.exists('Item', "TDS Item"): frappe.get_doc({ "doctype": "Item", @@ -178,7 +248,16 @@ def create_records(): "is_stock_item": 0, }).insert() - # create an account + if not frappe.db.exists('Item', "TCS Item"): + frappe.get_doc({ + "doctype": "Item", + "item_code": "TCS Item", + "item_name": "TCS Item", + "item_group": "All Item Groups", + "is_stock_item": 1 + }).insert() + + # create tds account if not frappe.db.exists("Account", "TDS - _TC"): frappe.get_doc({ 'doctype': 'Account', @@ -189,6 +268,17 @@ def create_records(): 'root_type': 'Asset' }).insert() + # create tcs account + if not frappe.db.exists("Account", "TCS - _TC"): + frappe.get_doc({ + 'doctype': 'Account', + 'company': '_Test Company', + 'account_name': 'TCS', + 'parent_account': 'Duties and Taxes - _TC', + 'report_type': 'Balance Sheet', + 'root_type': 'Liability' + }).insert() + def create_tax_with_holding_category(): fiscal_year = get_fiscal_year(today(), company="_Test Company")[0] @@ -210,6 +300,23 @@ def create_tax_with_holding_category(): }] }).insert() + if not frappe.db.exists("Tax Withholding Category", "Cumulative Threshold TCS"): + frappe.get_doc({ + "doctype": "Tax Withholding Category", + "name": "Cumulative Threshold TCS", + "category_name": "10% TCS", + "rates": [{ + 'fiscal_year': fiscal_year, + 'tax_withholding_rate': 10, + 'single_threshold': 0, + 'cumulative_threshold': 30000.00 + }], + "accounts": [{ + 'company': '_Test Company', + 'account': 'TCS - _TC' + }] + }).insert() + # Single thresold if not frappe.db.exists("Tax Withholding Category", "Single Threshold TDS"): frappe.get_doc({ From ae9a9a216785f29d6c3886f327385a4ab9428953 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 27 Jan 2021 19:24:42 +0530 Subject: [PATCH 572/728] fix: tcs chargable amount --- .../tax_withholding_category.py | 16 ++++++++++------ .../test_tax_withholding_category.py | 6 +++--- 2 files changed, 13 insertions(+), 9 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 36ed6decad..f2c4973caf 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -53,6 +53,10 @@ def get_party_tax_withholding_details(ref_doc, tax_withholding_category=None): frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}') .format(tax_withholding_category, ref_doc.company)) + if party_type == 'Customer' and not tax_details.cumulative_threshold: + frappe.throw(_('Tax Withholding Category {} against Company {} for Customer {} should have Cumulative Threshold value.') + .format(tax_withholding_category, ref_doc.company, party)) + tax_amount = get_tax_amount( party_type, parties, ref_doc, tax_details, @@ -145,7 +149,7 @@ def get_tax_amount(party_type, parties, ref_doc, tax_details, fiscal_year_detail tax_amount = grand_total * tax_details.rate / 100 if grand_total > 0 else 0 else: # if no tcs has been charged in FY, - # then (prev invoices + advances) value crossing the threshold are chargeable + # then chargeable value is "prev invoices + advances" value which cross the threshold tax_amount = get_tcs_amount( parties, ref_doc, tax_details, fiscal_year_details, vouchers, advance_vouchers @@ -253,13 +257,13 @@ def get_tcs_amount(parties, ref_doc, tax_details, fiscal_year_details, vouchers, 'voucher_type': 'Sales Invoice', }, 'sum(credit)') or 0.0 - current_invoice_total = get_invoice_total_without_tcs(ref_doc, tax_details) - chargeable_amt = current_invoice_total + invoiced_amt + advance_amt - credit_note_amt - - threshold = tax_details.get('threshold', 0) cumulative_threshold = tax_details.get('cumulative_threshold', 0) - if ((threshold and chargeable_amt >= threshold) or (cumulative_threshold and chargeable_amt >= cumulative_threshold)): + current_invoice_total = get_invoice_total_without_tcs(ref_doc, tax_details) + total_invoiced_amt = current_invoice_total + invoiced_amt + advance_amt - credit_note_amt + + if ((cumulative_threshold and total_invoiced_amt >= cumulative_threshold)): + chargeable_amt = total_invoiced_amt - cumulative_threshold tcs_amount = chargeable_amt * tax_details.rate / 100 if chargeable_amt > 0 else 0 return tcs_amount 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 c8bd0834d8..1d8fa4532f 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 @@ -140,13 +140,13 @@ class TestTaxWithholdingCategory(unittest.TestCase): # create another invoice whose total when added to previously created invoice, # surpasses cumulative threshhold - si = create_sales_invoice(customer = "Test TCS Customer") + si = create_sales_invoice(customer = "Test TCS Customer", rate=12000) si.submit() # assert tax collection on total invoice amount created until now tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC']) - self.assertEqual(tcs_charged, 3000) - self.assertEqual(si.grand_total, 13000) + self.assertEqual(tcs_charged, 200) + self.assertEqual(si.grand_total, 12200) invoices.append(si) # TCS is already collected once, so going forward system will collect TCS on every invoice From bc370b3ee586d97c2fd5c9db74a0718d4fff2d10 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 28 Jan 2021 11:44:26 +0530 Subject: [PATCH 573/728] chore: Set Stock Entry Form Indicators in setup - Makes it easier to override via customisations - Style consistency with other forms that set indicator in setup as well --- .../stock/doctype/stock_entry/stock_entry.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index bd2fce8bef..cb6cab3341 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -5,6 +5,14 @@ frappe.provide("erpnext.accounts.dimensions"); frappe.ui.form.on('Stock Entry', { setup: function(frm) { + frm.set_indicator_formatter('item_code', function(doc) { + if (!doc.s_warehouse) { + return 'blue'; + } else { + return (doc.qty<=doc.actual_qty) ? 'green' : 'orange' + } + }); + frm.set_query('work_order', function() { return { filters: [ @@ -779,15 +787,6 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ } } - this.frm.set_indicator_formatter('item_code', - function(doc) { - if (!doc.s_warehouse) { - return 'blue'; - } else { - return (doc.qty<=doc.actual_qty) ? "green" : "orange" - } - }) - this.frm.add_fetch("purchase_order", "supplier", "supplier"); frappe.dynamic_link = { doc: this.frm.doc, fieldname: 'supplier', doctype: 'Supplier' } From 55e7c5644cb17063f2ca79731cc71bebbaa48426 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 28 Jan 2021 12:00:18 +0530 Subject: [PATCH 574/728] fix:rfq contact email set --- .../doctype/request_for_quotation/request_for_quotation.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index a51498e935..7cf22f87e4 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -127,6 +127,10 @@ class RequestforQuotation(BuyingController): 'link_doctype': 'Supplier', 'link_name': rfq_supplier.supplier }) + contact.append('email_ids', { + 'email_id': user.name, + 'is_primary': 1 + }) if not contact.email_id and not contact.user: contact.email_id = user.name From 27bac2aaecfc831d899903f886a1feb3de357b91 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 28 Jan 2021 12:04:14 +0530 Subject: [PATCH 575/728] fix: Missing semi-colon --- erpnext/stock/doctype/stock_entry/stock_entry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index cb6cab3341..bc1d089899 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -9,7 +9,7 @@ frappe.ui.form.on('Stock Entry', { if (!doc.s_warehouse) { return 'blue'; } else { - return (doc.qty<=doc.actual_qty) ? 'green' : 'orange' + return (doc.qty<=doc.actual_qty) ? 'green' : 'orange'; } }); From bd9bdc5797d859988460a0e359205a12b89fe207 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 28 Jan 2021 12:05:57 +0530 Subject: [PATCH 576/728] feat: Batch wise item pricing (#24470) * feat: Batch wise item pricing * fix: Rate for items with no batch * fix: Add test case for batch pricing * fix: Sider Issues * fix: Add item filter for batch --- erpnext/public/js/controllers/buying.js | 4 + erpnext/public/js/controllers/transaction.js | 6 + erpnext/selling/sales_common.js | 4 + erpnext/stock/doctype/batch/test_batch.py | 70 ++++++++++- .../stock/doctype/item_price/item_price.js | 8 ++ .../stock/doctype/item_price/item_price.json | 116 ++++++++++++++---- .../stock/doctype/item_price/item_price.py | 5 +- erpnext/stock/get_item_details.py | 5 +- 8 files changed, 187 insertions(+), 31 deletions(-) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 0c6bcad721..a2a723dd77 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -195,6 +195,10 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ this._super(doc, cdt, cdn); }, + batch_no: function(doc, cdt, cdn) { + this._super(doc, cdt, cdn); + }, + received_qty: function(doc, cdt, cdn) { this.calculate_accepted_qty(doc, cdt, cdn) }, diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 26280998f9..77318332d2 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1099,6 +1099,11 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, + batch_no: function(doc, cdt, cdn) { + let item = frappe.get_doc(cdt, cdn); + this.apply_price_list(item, true); + }, + toggle_conversion_factor: function(item) { // toggle read only property for conversion factor field if the uom and stock uom are same if(this.frm.get_field('items').grid.fields_map.conversion_factor) { @@ -1403,6 +1408,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ "pricing_rules": d.pricing_rules, "warehouse": d.warehouse, "serial_no": d.serial_no, + "batch_no": d.batch_no, "price_list_rate": d.price_list_rate, "conversion_factor": d.conversion_factor || 1.0 }); diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 7f00fca8f0..ce084646e1 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -399,6 +399,10 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ } }, + batch_no: function(doc, cdt, cdn) { + this._super(doc, cdt, cdn); + }, + qty: function(doc, cdt, cdn) { this._super(doc, cdt, cdn); diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py index e41f1a8aaa..97f85bafd9 100644 --- a/erpnext/stock/doctype/batch/test_batch.py +++ b/erpnext/stock/doctype/batch/test_batch.py @@ -8,6 +8,8 @@ import unittest from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no from frappe.utils import cint, flt +from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice +from erpnext.stock.get_item_details import get_item_details class TestBatch(unittest.TestCase): def test_item_has_batch_enabled(self): @@ -182,7 +184,7 @@ class TestBatch(unittest.TestCase): stock_entry.cancel() current_batch_qty = flt(frappe.db.get_value("Batch", "B100", "batch_qty")) self.assertEqual(current_batch_qty, existing_batch_qty) - + @classmethod def make_new_batch_and_entry(cls, item_name, batch_name, warehouse): '''Make a new stock entry for given target warehouse and batch name of item''' @@ -252,6 +254,72 @@ class TestBatch(unittest.TestCase): return batch + def test_batch_wise_item_price(self): + if not frappe.db.get_value('Item', '_Test Batch Price Item'): + frappe.get_doc({ + 'doctype': 'Item', + 'is_stock_item': 1, + 'item_code': '_Test Batch Price Item', + 'item_group': 'Products', + 'has_batch_no': 1, + 'create_new_batch': 1 + }).insert(ignore_permissions=True) + + batch1 = create_batch('_Test Batch Price Item', 200, 1) + batch2 = create_batch('_Test Batch Price Item', 300, 1) + batch3 = create_batch('_Test Batch Price Item', 400, 0) + + args = frappe._dict({ + "item_code": "_Test Batch Price Item", + "company": "_Test Company with perpetual inventory", + "price_list": "_Test Price List", + "currency": "_Test Currency", + "doctype": "Sales Invoice", + "conversion_rate": 1, + "price_list_currency": "_Test Currency", + "plc_conversion_rate": 1, + "customer": "_Test Customer", + "name": None + }) + + #test price for batch1 + args.update({'batch_no': batch1}) + details = get_item_details(args) + self.assertEqual(details.get('price_list_rate'), 200) + + #test price for batch2 + args.update({'batch_no': batch2}) + details = get_item_details(args) + self.assertEqual(details.get('price_list_rate'), 300) + + #test price for batch3 + args.update({'batch_no': batch3}) + details = get_item_details(args) + self.assertEqual(details.get('price_list_rate'), 400) + +def create_batch(item_code, rate, create_item_price_for_batch): + pi = make_purchase_invoice(company="_Test Company with perpetual inventory", + warehouse= "Stores - TCP1", cost_center = "Main - TCP1", update_stock=1, + expense_account ="_Test Account Cost for Goods Sold - TCP1", item_code=item_code) + + batch = frappe.db.get_value('Batch', {'item': item_code, 'reference_name': pi.name}) + + if not create_item_price_for_batch: + create_price_list_for_batch(item_code, None, rate) + else: + create_price_list_for_batch(item_code, batch, rate) + + return batch + +def create_price_list_for_batch(item_code, batch, rate): + frappe.get_doc({ + 'doctype': 'Item Price', + 'item_code': '_Test Batch Price Item', + 'price_list': '_Test Price List', + 'batch_no': batch, + 'price_list_rate': rate + }).insert() + def make_new_batch(**args): args = frappe._dict(args) diff --git a/erpnext/stock/doctype/item_price/item_price.js b/erpnext/stock/doctype/item_price/item_price.js index 2729f4b15e..e4db0480db 100644 --- a/erpnext/stock/doctype/item_price/item_price.js +++ b/erpnext/stock/doctype/item_price/item_price.js @@ -15,5 +15,13 @@ frappe.ui.form.on("Item Price", { frm.set_df_property("bulk_import_help", "options", '' + __("Import in Bulk") + ''); + + frm.set_query('batch_no', function() { + return { + filters: { + 'item': frm.doc.item_code + } + } + }); } }); diff --git a/erpnext/stock/doctype/item_price/item_price.json b/erpnext/stock/doctype/item_price/item_price.json index 5f62381f8b..83177b372a 100644 --- a/erpnext/stock/doctype/item_price/item_price.json +++ b/erpnext/stock/doctype/item_price/item_price.json @@ -18,6 +18,7 @@ "price_list", "customer", "supplier", + "batch_no", "column_break_3", "buying", "selling", @@ -47,31 +48,41 @@ "oldfieldtype": "Select", "options": "Item", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "uom", "fieldtype": "Link", "label": "UOM", - "options": "UOM" + "options": "UOM", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "description": "Quantity that must be bought or sold per UOM", "fieldname": "packing_unit", "fieldtype": "Int", - "label": "Packing Unit" + "label": "Packing Unit", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_17", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "item_name", "fieldtype": "Data", "in_list_view": 1, "label": "Item Name", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "item_code.brand", @@ -79,19 +90,25 @@ "fieldtype": "Read Only", "in_list_view": 1, "label": "Brand", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "item_description", "fieldtype": "Text", "label": "Item Description", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_details", "fieldtype": "Section Break", "label": "Price List", - "options": "fa fa-tags" + "options": "fa fa-tags", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list", @@ -100,7 +117,9 @@ "in_standard_filter": 1, "label": "Price List", "options": "Price List", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -108,37 +127,49 @@ "fieldname": "customer", "fieldtype": "Link", "label": "Customer", - "options": "Customer" + "options": "Customer", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.buying == 1", "fieldname": "supplier", "fieldtype": "Link", "label": "Supplier", - "options": "Supplier" + "options": "Supplier", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_3", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "buying", "fieldtype": "Check", "label": "Buying", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "selling", "fieldtype": "Check", "label": "Selling", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "item_details", "fieldtype": "Section Break", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -146,11 +177,15 @@ "fieldtype": "Link", "label": "Currency", "options": "Currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_br_1", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_rate", @@ -162,53 +197,80 @@ "oldfieldname": "ref_rate", "oldfieldtype": "Currency", "options": "currency", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_15", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Today", "fieldname": "valid_from", "fieldtype": "Date", - "label": "Valid From" + "label": "Valid From", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "lead_time_days", "fieldtype": "Int", - "label": "Lead Time in days" + "label": "Lead Time in days", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_18", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "valid_upto", "fieldtype": "Date", - "label": "Valid Upto" + "label": "Valid Upto", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_24", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "note", "fieldtype": "Text", - "label": "Note" + "label": "Note", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "reference", "fieldtype": "Data", "in_list_view": 1, - "label": "Reference" + "label": "Reference", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "batch_no", + "fieldtype": "Link", + "label": "Batch No", + "options": "Batch", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-flag", "idx": 1, + "index_web_pages_for_search": 1, "links": [], - "modified": "2020-07-06 22:31:32.943475", + "modified": "2020-12-08 18:12:15.395772", "modified_by": "Administrator", "module": "Stock", "name": "Item Price", diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index bed5ea9ab6..e82a19b0dc 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -54,7 +54,8 @@ class ItemPrice(Document): "valid_upto", "packing_unit", "customer", - "supplier",]: + "supplier", + "batch_no"]: if self.get(field): conditions += " and {0} = %({0})s ".format(field) else: @@ -68,7 +69,7 @@ class ItemPrice(Document): 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,) + frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, Batch, UOM, Qty, and Dates."), ItemPriceDuplicateItem,) def before_save(self): if self.selling: diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index bf45251c9d..dfe8fea67b 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -674,6 +674,8 @@ def get_item_price(args, item_code, ignore_party=False): and price_list=%(price_list)s and ifnull(uom, '') in ('', %(uom)s)""" + conditions += "and ifnull(batch_no, '') in ('', %(batch_no)s)" + if not ignore_party: if args.get("customer"): conditions += " and customer=%(customer)s" @@ -692,7 +694,7 @@ def get_item_price(args, item_code, ignore_party=False): return frappe.db.sql(""" select name, price_list_rate, uom from `tabItem Price` {conditions} - order by valid_from desc, uom desc """.format(conditions=conditions), args) + order by valid_from desc, batch_no desc, uom desc """.format(conditions=conditions), args) def get_price_list_rate_for(args, item_code): """ @@ -711,6 +713,7 @@ def get_price_list_rate_for(args, item_code): "uom": args.get('uom'), "transaction_date": args.get('transaction_date'), "posting_date": args.get('posting_date'), + "batch_no": args.get('batch_no') } item_price_data = 0 From 16bcd01f6da00016130183c8552ddd3256a0ca70 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 28 Jan 2021 12:09:35 +0530 Subject: [PATCH 577/728] feat: Provision to disable serial no and batch selector (#24398) * feat: feat: Provision to disbale serial no and batch selector * fix: Sider issues * fix: Linting issues --- erpnext/public/js/controllers/transaction.js | 12 +++++++++++- .../stock/doctype/stock_settings/stock_settings.json | 9 ++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 77318332d2..1d1f18b1a0 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -589,11 +589,21 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) .then((r) => { if (r.message && - (r.message.has_batch_no || r.message.has_serial_no)) { + (r.message.has_batch_no || r.message.has_serial_no)) { frappe.flags.hide_serial_batch_dialog = false; } }); }, + () => { + // check if batch serial selector is disabled or not + if (show_batch_dialog && !frappe.flags.hide_serial_batch_dialog) + return frappe.db.get_single_value('Stock Settings', 'disable_serial_no_and_batch_selector') + .then((value) => { + if (value) { + frappe.flags.hide_serial_batch_dialog = true; + } + }); + }, () => { if(show_batch_dialog && !frappe.flags.hide_serial_batch_dialog) { var d = locals[cdt][cdn]; diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index 3ff396ba77..84af57b48d 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -16,6 +16,7 @@ "action_if_quality_inspection_is_not_submitted", "show_barcode_field", "clean_description_html", + "disable_serial_no_and_batch_selector", "section_break_7", "auto_insert_price_list_rate_if_missing", "allow_negative_stock", @@ -227,6 +228,12 @@ "fieldname": "control_historical_stock_transactions_section", "fieldtype": "Section Break", "label": "Control Historical Stock Transactions" + }, + { + "default": "0", + "fieldname": "disable_serial_no_and_batch_selector", + "fieldtype": "Check", + "label": "Disable Serial No And Batch Selector" } ], "icon": "icon-cog", @@ -234,7 +241,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2020-12-29 12:53:31.162247", + "modified": "2021-01-18 13:15:38.352796", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", From f6f4376463456c980fe8a380609484f11e577689 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Thu, 28 Jan 2021 07:46:26 +0100 Subject: [PATCH 578/728] feat: remove german sales invoice validation (#24441) This can be better achieved via Customize Form (mandatory and mandatory_depends_on) --- erpnext/hooks.py | 3 -- .../regional/germany/accounts_controller.py | 53 ------------------- .../germany/test_accounts_controller.py | 12 ----- 3 files changed, 68 deletions(-) delete mode 100644 erpnext/regional/germany/accounts_controller.py delete mode 100644 erpnext/regional/germany/test_accounts_controller.py diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 14377e1b99..1c20555b82 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -416,9 +416,6 @@ regional_overrides = { 'Italy': { 'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.italy.utils.update_itemised_tax_data', 'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.italy.utils.sales_invoice_validate', - }, - 'Germany': { - 'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.germany.accounts_controller.validate_regional', } } user_privacy_documents = [ diff --git a/erpnext/regional/germany/accounts_controller.py b/erpnext/regional/germany/accounts_controller.py deleted file mode 100644 index 7f76493608..0000000000 --- a/erpnext/regional/germany/accounts_controller.py +++ /dev/null @@ -1,53 +0,0 @@ -import frappe -from frappe import _ -from frappe import msgprint - - -REQUIRED_FIELDS = { - "Sales Invoice": [ - { - "field_name": "company_address", - "regulation": "§ 14 Abs. 4 Nr. 1 UStG" - }, - { - "field_name": "company_tax_id", - "regulation": "§ 14 Abs. 4 Nr. 2 UStG" - }, - { - "field_name": "taxes", - "regulation": "§ 14 Abs. 4 Nr. 8 UStG" - }, - { - "field_name": "customer_address", - "regulation": "§ 14 Abs. 4 Nr. 1 UStG", - "condition": "base_grand_total > 250" - } - ] -} - - -def validate_regional(doc): - """Check if required fields for this document are present.""" - required_fields = REQUIRED_FIELDS.get(doc.doctype) - if not required_fields: - return - - meta = frappe.get_meta(doc.doctype) - field_map = {field.fieldname: field.label for field in meta.fields} - - for field in required_fields: - condition = field.get("condition") - if condition and not frappe.safe_eval(condition, doc.as_dict()): - continue - - field_name = field.get("field_name") - regulation = field.get("regulation") - if field_name and not doc.get(field_name): - missing(field_map.get(field_name), regulation) - - -def missing(field_label, regulation): - """Notify the user that a required field is missing.""" - translated_msg = _('Remember to set {field_label}. It is required by {regulation}.', context='Specific for Germany. Example: Remember to set Company Tax ID. It is required by § 14 Abs. 4 Nr. 2 UStG.') # noqa: E501 - formatted_msg = translated_msg.format(field_label=frappe.bold(_(field_label)), regulation=regulation) - msgprint(formatted_msg) diff --git a/erpnext/regional/germany/test_accounts_controller.py b/erpnext/regional/germany/test_accounts_controller.py deleted file mode 100644 index 8bd378c971..0000000000 --- a/erpnext/regional/germany/test_accounts_controller.py +++ /dev/null @@ -1,12 +0,0 @@ -import frappe -import unittest -from erpnext.regional.germany.accounts_controller import validate_regional - - -class TestAccountsController(unittest.TestCase): - - def setUp(self): - self.sales_invoice = frappe.get_last_doc('Sales Invoice') - - def test_validate_regional(self): - validate_regional(self.sales_invoice) From e5529ad461e5f57c972c880371308e4e8db0f62a Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Thu, 28 Jan 2021 12:22:39 +0530 Subject: [PATCH 579/728] fix: validate tax template for tax category (#24402) * fix: validate tax template for tax category * Update sales_taxes_and_charges_template.py --- .../sales_taxes_and_charges_template.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py index b46de6c85b..429a9f3591 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py @@ -34,6 +34,9 @@ def valdiate_taxes_and_charges_template(doc): validate_disabled(doc) + # Validate with existing taxes and charges template for unique tax category + validate_for_tax_category(doc) + for tax in doc.get("taxes"): validate_taxes_and_charges(tax) validate_inclusive_tax(tax, doc) @@ -41,3 +44,7 @@ def valdiate_taxes_and_charges_template(doc): def validate_disabled(doc): if doc.is_default and doc.disabled: frappe.throw(_("Disabled template must not be default template")) + +def validate_for_tax_category(doc): + if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0}): + frappe.throw(_("A template with tax category {0} already exists. Only one template is allowed with each tax category").format(frappe.bold(doc.tax_category))) From 5756bf50e6d1d41cfbf7b028ee59baa7d48ebf3c Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Thu, 28 Jan 2021 12:24:39 +0530 Subject: [PATCH 580/728] fix: removed all day event from shift assignment calendar (#24397) Co-authored-by: Afshan <33727827+AfshanKhan@users.noreply.github.com> --- .../shift_assignment/shift_assignment.py | 48 ++++++++++++++----- .../shift_assignment_calendar.js | 10 +--- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py index 2c385e80f4..ab65260c09 100644 --- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py +++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py @@ -88,7 +88,7 @@ def get_events(start, end, filters=None): def add_assignments(events, start, end, conditions=None): query = """select name, start_date, end_date, employee_name, - employee, docstatus + employee, docstatus, shift_type from `tabShift Assignment` where start_date >= %(start_date)s or end_date <= %(end_date)s @@ -97,18 +97,40 @@ def add_assignments(events, start, end, conditions=None): if conditions: query += conditions - for d in frappe.db.sql(query, {"start_date":start, "end_date":end}, as_dict=True): - e = { - "name": d.name, - "doctype": "Shift Assignment", - "start_date": d.start_date, - "end_date": d.end_date if d.end_date else nowdate(), - "title": cstr(d.employee_name) + ": "+ \ - cstr(d.shift_type), - "docstatus": d.docstatus - } - if e not in events: - events.append(e) + records = frappe.db.sql(query, {"start_date":start, "end_date":end}, as_dict=True) + shift_timing_map = get_shift_type_timing([d.shift_type for d in records]) + + for d in records: + daily_event_start = d.start_date + daily_event_end = d.end_date if d.end_date else getdate() + delta = timedelta(days=1) + while daily_event_start <= daily_event_end: + start_timing = frappe.utils.get_datetime(daily_event_start)+ shift_timing_map[d.shift_type]['start_time'] + end_timing = frappe.utils.get_datetime(daily_event_start)+ shift_timing_map[d.shift_type]['end_time'] + daily_event_start += delta + e = { + "name": d.name, + "doctype": "Shift Assignment", + "start_date": start_timing, + "end_date": end_timing, + "title": cstr(d.employee_name) + ": "+ \ + cstr(d.shift_type), + "docstatus": d.docstatus, + "allDay": 0 + } + if e not in events: + events.append(e) + + return events + +def get_shift_type_timing(shift_types): + shift_timing_map = {} + data = frappe.get_all("Shift Type", filters = {"name": ("IN", shift_types)}, fields = ['name', 'start_time', 'end_time']) + + for d in data: + shift_timing_map[d.name] = d + + return shift_timing_map def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=False, next_shift_direction=None): diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js b/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js index 17a986deb2..bb692e1402 100644 --- a/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js +++ b/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js @@ -6,14 +6,8 @@ frappe.views.calendar["Shift Assignment"] = { "start": "start_date", "end": "end_date", "id": "name", - "docstatus": 1 - }, - options: { - header: { - left: 'prev,next today', - center: 'title', - right: 'month' - } + "docstatus": 1, + "allDay": "allDay", }, get_events_method: "erpnext.hr.doctype.shift_assignment.shift_assignment.get_events" } \ No newline at end of file From 3914aca647f25592dffe66acb3d35b41a11f2a0c Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Thu, 28 Jan 2021 12:25:15 +0530 Subject: [PATCH 581/728] fix: check for tax rate (#24376) --- erpnext/regional/report/gstr_1/gstr_1.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index ad3de5f398..96dc3f728d 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -255,15 +255,16 @@ class Gstr1Report(object): for item_code, tax_amounts in item_wise_tax_detail.items(): tax_rate = tax_amounts[0] - if cgst_or_sgst: - tax_rate *= 2 - if parent not in self.cgst_sgst_invoices: - self.cgst_sgst_invoices.append(parent) + if tax_rate: + if cgst_or_sgst: + tax_rate *= 2 + if parent not in self.cgst_sgst_invoices: + self.cgst_sgst_invoices.append(parent) - rate_based_dict = self.items_based_on_tax_rate\ - .setdefault(parent, {}).setdefault(tax_rate, []) - if item_code not in rate_based_dict: - rate_based_dict.append(item_code) + rate_based_dict = self.items_based_on_tax_rate\ + .setdefault(parent, {}).setdefault(tax_rate, []) + if item_code not in rate_based_dict: + rate_based_dict.append(item_code) except ValueError: continue if unidentified_gst_accounts: From 8e55677f10fac3002983a4fbf9160dd5a9e53cb0 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 28 Jan 2021 12:26:45 +0530 Subject: [PATCH 582/728] feat: deleting account & stock entries on deletion of transaction (#24298) * feat: control for deleting account & stock entries on deletion of a transaction * chore: change label & fieldname Co-authored-by: Nabin Hait --- .../doctype/accounts_settings/accounts_settings.json | 11 +++++++++-- erpnext/controllers/accounts_controller.py | 6 ++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 41f9ce030a..a3c29b6d64 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -21,6 +21,7 @@ "book_asset_depreciation_entry_automatically", "add_taxes_from_item_tax_template", "automatically_fetch_payment_terms", + "delete_linked_ledger_entries", "deferred_accounting_settings_section", "automatically_process_deferred_accounting_entry", "book_deferred_entries_based_on", @@ -219,6 +220,12 @@ "fieldtype": "Select", "label": "Book Deferred Entries Based On", "options": "Days\nMonths" + }, + { + "default": "0", + "fieldname": "delete_linked_ledger_entries", + "fieldtype": "Check", + "label": "Delete Accounting and Stock Ledger Entries on deletion of Transaction" } ], "icon": "icon-cog", @@ -226,7 +233,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2020-10-13 11:32:52.268826", + "modified": "2021-01-05 13:04:00.118892", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", @@ -254,4 +261,4 @@ "sort_field": "modified", "sort_order": "ASC", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 0f1aa23064..b9bfbaf0bd 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -118,6 +118,12 @@ class AccountsController(TransactionBase): def before_cancel(self): validate_einvoice_fields(self) + + def on_trash(self): + # delete sl and gl entries on deletion of transaction + if frappe.db.get_single_value('Accounts Settings', 'delete_linked_ledger_entries'): + frappe.db.sql("delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name)) + frappe.db.sql("delete from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name)) def validate_deferred_start_and_end_date(self): for d in self.items: From 9f8cbe91013c49126c8be3b4164343fd1ee6eab9 Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Thu, 28 Jan 2021 12:29:52 +0530 Subject: [PATCH 583/728] Material request wrong status issue (#24019) * fix: material request status fix * fix: changing precision to global defaults for material request status * fix: adding back the removed flt * fix: sider issues fixed Co-authored-by: pateljannat --- .../doctype/purchase_order/purchase_order.js | 2 +- .../request_for_quotation.js | 4 ++-- .../supplier_quotation/supplier_quotation.js | 2 +- .../material_request/material_request_list.js | 15 ++++++++------- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 14534ef1b7..83efa66f6b 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -381,7 +381,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( material_request_type: "Purchase", docstatus: 1, status: ["!=", "Stopped"], - per_ordered: ["<", 99.99], + per_ordered: ["<", 100], company: me.frm.doc.company } }) 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 e537771eaf..b76c3784a4 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -224,7 +224,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e material_request_type: "Purchase", docstatus: 1, status: ["!=", "Stopped"], - per_ordered: ["<", 99.99], + per_ordered: ["<", 100], company: me.frm.doc.company } }) @@ -280,7 +280,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e material_request_type: "Purchase", docstatus: 1, status: ["!=", "Stopped"], - per_ordered: ["<", 99.99] + per_ordered: ["<", 100] } }); dialog.hide(); diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js index a3b2085400..a0187b0a82 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js @@ -44,7 +44,7 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext material_request_type: "Purchase", docstatus: 1, status: ["!=", "Stopped"], - per_ordered: ["<", 99.99], + per_ordered: ["<", 100], company: me.frm.doc.company } }) diff --git a/erpnext/stock/doctype/material_request/material_request_list.js b/erpnext/stock/doctype/material_request/material_request_list.js index 0d7095875c..de7a3d05bf 100644 --- a/erpnext/stock/doctype/material_request/material_request_list.js +++ b/erpnext/stock/doctype/material_request/material_request_list.js @@ -1,9 +1,10 @@ frappe.listview_settings['Material Request'] = { add_fields: ["material_request_type", "status", "per_ordered", "per_received", "transfer_status"], get_indicator: function(doc) { - if(doc.status=="Stopped") { + var precision = frappe.defaults.get_default("float_precision"); + if (doc.status=="Stopped") { return [__("Stopped"), "red", "status,=,Stopped"]; - } else if(doc.transfer_status && doc.docstatus != 2) { + } else if (doc.transfer_status && doc.docstatus != 2) { if (doc.transfer_status == "Not Started") { return [__("Not Started"), "orange"]; } else if (doc.transfer_status == "In Transit") { @@ -11,14 +12,14 @@ frappe.listview_settings['Material Request'] = { } else if (doc.transfer_status == "Completed") { return [__("Completed"), "green"]; } - } else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 0) { + } else if (doc.docstatus==1 && flt(doc.per_ordered, precision) == 0) { return [__("Pending"), "orange", "per_ordered,=,0"]; - } else if(doc.docstatus==1 && flt(doc.per_ordered, 2) < 100) { + } else if (doc.docstatus==1 && flt(doc.per_ordered, precision) < 100) { return [__("Partially ordered"), "yellow", "per_ordered,<,100"]; - } else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 100) { - if (doc.material_request_type == "Purchase" && flt(doc.per_received, 2) < 100 && flt(doc.per_received, 2) > 0) { + } else if (doc.docstatus==1 && flt(doc.per_ordered, precision) == 100) { + if (doc.material_request_type == "Purchase" && flt(doc.per_received, precision) < 100 && flt(doc.per_received, precision) > 0) { return [__("Partially Received"), "yellow", "per_received,<,100"]; - } else if (doc.material_request_type == "Purchase" && flt(doc.per_received, 2) == 100) { + } else if (doc.material_request_type == "Purchase" && flt(doc.per_received, precision) == 100) { return [__("Received"), "green", "per_received,=,100"]; } else if (doc.material_request_type == "Purchase") { return [__("Ordered"), "green", "per_ordered,=,100"]; From f7ea340181acd3fdde2c185a8d1c36fb75e04e6e Mon Sep 17 00:00:00 2001 From: Mohamed Almubarak <59777434+madar2020@users.noreply.github.com> Date: Thu, 28 Jan 2021 10:02:13 +0300 Subject: [PATCH 584/728] fix(template): cards on second row overlaps the one before (#23454) * Update macros.html fix second row of cards overlapps with the one before * Update macros.html fix(template): second row of cards overlaps with the one before * Update macros.html fix(template): second row of cards overlaps with the one before --- erpnext/templates/includes/macros.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html index ea6b00fc58..5d8ee5cab6 100644 --- a/erpnext/templates/includes/macros.html +++ b/erpnext/templates/includes/macros.html @@ -40,7 +40,7 @@
    {% if card.image %} -
    +
    {% endif %}
    {{ card.title }}
    From b4be2922173ed74a04a02cfb5d1fdd5976459f85 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 28 Jan 2021 13:09:56 +0530 Subject: [PATCH 585/728] fix: Item valuation for internal stock transfers (#24200) * fix: Item valuation for internal stocktransfers * fix: Consider conversion factor for invoices * fix: Add missing set warehouse fields * fix: Add validations and other fixes * fix: Fixes in flow * fix: Internal sales item link in Purchase Invoice * fix: Debugging * fix: Debug travis * fix: Remove commented code * fix: Rate forcing in sales order * fix: Unreallized profit in Sales Register * fix: Item wise gst sales register fix * fix: From warehouse in Purchase Order * fix: Target field in Sales Invoice * fix: remove self * fix: GST Purchasse register and other fixes * fix: Add shipping_address to no field map * fix: Ref doc map * fix: Test Cases * fix: address mapping between sales and purchase document * fix: Import Error * fix: Tax updation for internal invoices * fix: Purchase Order and Invoice linking * fix: Internal Party validation fix * fix: validation * fix(india): GST Taxes for intra state transfer * fix(india): GST Taxes for intra state transfer Co-authored-by: Nabin Hait --- .../purchase_invoice/purchase_invoice.js | 5 +- .../purchase_invoice/purchase_invoice.json | 30 ++-- .../purchase_invoice/purchase_invoice.py | 54 +++--- .../purchase_invoice_item.json | 26 ++- .../doctype/sales_invoice/sales_invoice.js | 19 +-- .../doctype/sales_invoice/sales_invoice.json | 15 +- .../doctype/sales_invoice/sales_invoice.py | 157 ++++++++++++++++-- .../sales_invoice/test_sales_invoice.py | 114 ++++++++----- .../sales_invoice_item.json | 5 +- .../item_wise_purchase_register.py | 4 +- .../item_wise_sales_register.py | 3 +- .../purchase_register/purchase_register.py | 41 ++++- .../report/sales_register/sales_register.py | 59 ++++++- .../doctype/purchase_order/purchase_order.js | 20 +-- .../purchase_order/purchase_order.json | 19 ++- erpnext/buying/doctype/supplier/supplier.py | 5 +- erpnext/controllers/accounts_controller.py | 22 ++- erpnext/controllers/buying_controller.py | 52 +++++- erpnext/controllers/selling_controller.py | 28 +++- erpnext/controllers/stock_controller.py | 30 +++- erpnext/public/js/controllers/transaction.js | 49 +++++- erpnext/public/js/utils/party.js | 6 + erpnext/regional/india/taxes.js | 6 +- erpnext/regional/india/utils.py | 2 +- erpnext/selling/doctype/customer/customer.py | 5 +- .../doctype/sales_order/sales_order.js | 23 +-- .../doctype/sales_order/sales_order.json | 23 ++- .../doctype/delivery_note/delivery_note.js | 29 ++-- .../doctype/delivery_note/delivery_note.json | 38 +++-- .../doctype/delivery_note/delivery_note.py | 31 +++- .../delivery_note_item.json | 7 +- .../purchase_receipt/purchase_receipt.json | 22 ++- .../purchase_receipt_item.json | 14 +- erpnext/stock/stock_ledger.py | 29 ++-- 34 files changed, 749 insertions(+), 243 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 4a952a30a2..06aa20bfc5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -275,8 +275,11 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ supplier: function() { var me = this; - if(this.frm.updating_party_details) + + // Do not update if inter company reference is there as the details will already be updated + if(this.frm.updating_party_details || this.frm.doc.inter_company_invoice_reference) return; + erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details", { posting_date: this.frm.doc.posting_date, diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index c64ffd878c..451c936881 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -57,8 +57,8 @@ "set_warehouse", "rejected_warehouse", "col_break_warehouse", + "set_from_warehouse", "is_subcontracted", - "supplier_warehouse", "items_section", "update_stock", "scan_barcode", @@ -515,6 +515,7 @@ }, { "depends_on": "update_stock", + "description": "Sets 'Accepted Warehouse' in each row of the items table.", "fieldname": "set_warehouse", "fieldtype": "Link", "label": "Set Accepted Warehouse", @@ -543,17 +544,6 @@ "options": "No\nYes", "print_hide": 1 }, - { - "depends_on": "eval:doc.is_subcontracted==\"Yes\"", - "fieldname": "supplier_warehouse", - "fieldtype": "Link", - "label": "Supplier Warehouse", - "no_copy": 1, - "options": "Warehouse", - "print_hide": 1, - "print_width": "50px", - "width": "50px" - }, { "fieldname": "items_section", "fieldtype": "Section Break", @@ -1232,7 +1222,9 @@ "fieldname": "inter_company_invoice_reference", "fieldtype": "Link", "label": "Inter Company Invoice Reference", + "no_copy": 1, "options": "Sales Invoice", + "print_hide": 1, "read_only": 1 }, { @@ -1356,13 +1348,25 @@ "fieldtype": "Link", "label": "Represents Company", "options": "Company" + }, + { + "depends_on": "eval:doc.update_stock && (doc.is_subcontracted==\"Yes\" || doc.is_internal_supplier)", + "description": "Sets 'From Warehouse' in each row of the items table.", + "fieldname": "set_from_warehouse", + "fieldtype": "Link", + "label": "Set From Warehouse", + "no_copy": 1, + "options": "Warehouse", + "print_hide": 1, + "print_width": "50px", + "width": "50px" } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2020-12-11 12:46:12.796378", + "modified": "2020-12-26 20:49:03.305063", "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 b52678e8d3..56feb573c2 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -443,7 +443,7 @@ class PurchaseInvoice(BuyingController): else: self.stock_received_but_not_billed = None self.expenses_included_in_valuation = None - + self.negative_expense_to_be_booked = 0.0 gl_entries = [] @@ -457,7 +457,7 @@ class PurchaseInvoice(BuyingController): self.make_internal_transfer_gl_entries(gl_entries) gl_entries = make_regional_gl_entries(gl_entries, self) - + gl_entries = merge_similar_entries(gl_entries) self.make_payment_gl_entries(gl_entries) @@ -480,7 +480,7 @@ class PurchaseInvoice(BuyingController): grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total if grand_total and not self.is_internal_transfer(): - # Didnot use base_grand_total to book rounding loss gle + # Did not 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( @@ -511,8 +511,8 @@ class PurchaseInvoice(BuyingController): voucher_wise_stock_value = {} if self.update_stock: for d in frappe.get_all('Stock Ledger Entry', - fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}): - voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference) + fields = ["voucher_detail_no", "stock_value_difference", "warehouse"], filters={'voucher_no': self.name}): + voucher_wise_stock_value.setdefault((d.voucher_detail_no, d.warehouse), d.stock_value_difference) valuation_tax_accounts = [d.account_head for d in self.get("taxes") if d.category in ('Valuation', 'Total and Valuation') @@ -563,16 +563,17 @@ class PurchaseInvoice(BuyingController): ) else: - gl_entries.append( - self.get_gl_dict({ - "account": item.expense_account, - "against": self.supplier, - "debit": warehouse_debit_amount, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "cost_center": item.cost_center, - "project": item.project or self.project - }, account_currency, item=item) - ) + if not self.is_internal_transfer(): + gl_entries.append( + self.get_gl_dict({ + "account": item.expense_account, + "against": self.supplier, + "debit": warehouse_debit_amount, + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "cost_center": item.cost_center, + "project": item.project or self.project + }, account_currency, item=item) + ) # Amount added through landed-cost-voucher if landed_cost_entries: @@ -624,13 +625,14 @@ class PurchaseInvoice(BuyingController): if expense_booked_in_pr: expense_account = service_received_but_not_billed_account - gl_entries.append(self.get_gl_dict({ - "account": expense_account, - "against": self.supplier, - "debit": amount, - "cost_center": item.cost_center, - "project": item.project or self.project - }, account_currency, item=item)) + if not self.is_internal_transfer(): + gl_entries.append(self.get_gl_dict({ + "account": expense_account, + "against": self.supplier, + "debit": amount, + "cost_center": item.cost_center, + "project": item.project or self.project + }, account_currency, item=item)) # If asset is bought through this document and not linked to PR if self.update_stock and item.landed_cost_voucher_amount: @@ -795,10 +797,10 @@ class PurchaseInvoice(BuyingController): # Stock ledger value is not matching with the warehouse amount if (self.update_stock and voucher_wise_stock_value.get(item.name) and - warehouse_debit_amount != flt(voucher_wise_stock_value.get(item.name), net_amt_precision)): + warehouse_debit_amount != flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)): cost_of_goods_sold_account = self.get_company_default("default_expense_account") - stock_amount = flt(voucher_wise_stock_value.get(item.name), net_amt_precision) + stock_amount = flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision) stock_adjustment_amt = warehouse_debit_amount - stock_amount gl_entries.append( @@ -999,10 +1001,10 @@ class PurchaseInvoice(BuyingController): self.delete_auto_created_batches() self.make_gl_entries_on_cancel() - + if self.update_stock == 1: self.repost_future_sle_and_gle() - + self.update_project() frappe.db.set(self, 'status', 'Cancelled') diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index f6d76e5050..1f7853dbf7 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-05-22 12:43:10", "doctype": "DocType", @@ -87,6 +88,7 @@ "po_detail", "purchase_receipt", "pr_detail", + "sales_invoice_item", "item_weight_details", "weight_per_unit", "total_weight", @@ -553,8 +555,8 @@ "fieldtype": "Link", "hidden": 1, "label": "Brand", - "print_hide": 1, - "options": "Brand" + "options": "Brand", + "print_hide": 1 }, { "fetch_from": "item_code.item_group", @@ -562,9 +564,9 @@ "fieldname": "item_group", "fieldtype": "Link", "label": "Item Group", + "options": "Item Group", "print_hide": 1, - "read_only": 1, - "options": "Item Group" + "read_only": 1 }, { "description": "Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges", @@ -759,10 +761,11 @@ "read_only": 1 }, { + "depends_on": "eval:parent.is_internal_supplier && parent.update_stock", "fieldname": "from_warehouse", "fieldtype": "Link", "ignore_user_permissions": 1, - "label": "Supplier Warehouse", + "label": "From Warehouse", "options": "Warehouse" }, { @@ -779,11 +782,20 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "sales_invoice_item", + "fieldtype": "Data", + "label": "Sales Invoice Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2020-08-20 11:48:01.398356", + "links": [], + "modified": "2020-12-26 17:20:36.415791", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", @@ -791,4 +803,4 @@ "permissions": [], "sort_field": "modified", "sort_order": "DESC" -} +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 72199a92aa..f2a62cdacd 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -130,16 +130,15 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte this.set_default_print_format(); if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) { - frappe.model.with_doc("Customer", me.frm.doc.customer, function() { - var customer = frappe.model.get_doc("Customer", me.frm.doc.customer); - var internal = customer.is_internal_customer; - var disabled = customer.disabled; - if (internal == 1 && disabled == 0) { - me.frm.add_custom_button("Inter Company Invoice", function() { - me.make_inter_company_invoice(); - }, __('Create')); - } - }); + let internal = me.frm.doc.is_internal_customer; + if (internal) { + let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Invoice" : + "Inter Company Purchase Invoice"; + + me.frm.add_custom_button(button_label, function() { + me.make_inter_company_invoice(); + }, __('Create')); + } } }, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 6799fb986a..447cee42a7 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -60,6 +60,8 @@ "ignore_pricing_rule", "sec_warehouse", "set_warehouse", + "column_break_55", + "set_target_warehouse", "items_section", "update_stock", "scan_barcode", @@ -1969,13 +1971,24 @@ "label": "Represents Company", "options": "Company", "read_only": 1 + }, + { + "fieldname": "column_break_55", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval: doc.is_internal_customer && doc.update_stock", + "fieldname": "set_target_warehouse", + "fieldtype": "Link", + "label": "Set Target Warehouse", + "options": "Warehouse" } ], "icon": "fa fa-file-text", "idx": 181, "is_submittable": 1, "links": [], - "modified": "2020-12-11 12:48:31.769958", + "modified": "2020-12-25 22:57:32.555067", "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 566734e7d1..7116a6a62b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -6,7 +6,7 @@ import frappe, erpnext import frappe.defaults from frappe.utils import cint, flt, getdate, add_days, cstr, nowdate, get_link_to_form, formatdate from frappe import _, msgprint, throw -from erpnext.accounts.party import get_party_account, get_due_date +from erpnext.accounts.party import get_party_account, get_due_date, get_party_details from frappe.model.mapper import get_mapped_doc from erpnext.controllers.selling_controller import SellingController from erpnext.accounts.utils import get_account_currency @@ -21,6 +21,8 @@ from erpnext.accounts.general_ledger import get_round_off_account_and_cost_cente from erpnext.accounts.doctype.loyalty_program.loyalty_program import \ get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points from erpnext.accounts.deferred_revenue import validate_service_stop_date +from frappe.model.utils import get_fetch_values +from frappe.contacts.doctype.address.address import get_address_display from erpnext.healthcare.utils import manage_invoice_submit_cancel @@ -1534,7 +1536,7 @@ def validate_inter_company_transaction(doc, doctype): details = get_inter_company_details(doc, doctype) price_list = doc.selling_price_list if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"] else doc.buying_price_list valid_price_list = frappe.db.get_value("Price List", {"name": price_list, "buying": 1, "selling": 1}) - if not valid_price_list: + if not valid_price_list and not doc.is_internal_transfer(): frappe.throw(_("Selected Price List should have buying and selling fields checked.")) party = details.get("party") @@ -1557,6 +1559,7 @@ 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" + target_detail_field = "sales_invoice_item" if doctype == "Sales Invoice" else "sales_order_item" source_document_warehouse_field = 'target_warehouse' target_document_warehouse_field = 'from_warehouse' else: @@ -1570,6 +1573,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): def set_missing_values(source, target): target.run_method("set_missing_values") + set_purchase_references(target) def update_details(source_doc, target_doc, source_parent): target_doc.inter_company_invoice_reference = source_doc.name @@ -1577,19 +1581,38 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): currency = frappe.db.get_value('Supplier', details.get('party'), 'default_currency') target_doc.company = details.get("company") target_doc.supplier = details.get("party") + target_doc.is_internal_supplier = 1 + target_doc.ignore_pricing_rule = 1 target_doc.buying_price_list = source_doc.selling_price_list + # Invert Addresses + update_address(target_doc, 'supplier_address', 'address_display', source_doc.company_address) + update_address(target_doc, 'shipping_address', 'shipping_address_display', source_doc.customer_address) + if currency: target_doc.currency = currency + + update_taxes(target_doc, party=target_doc.supplier, party_type='Supplier', company=target_doc.company, + doctype=target_doc.doctype, party_address=target_doc.supplier_address, + company_address=target_doc.shipping_address) + else: currency = frappe.db.get_value('Customer', details.get('party'), 'default_currency') target_doc.company = details.get("company") target_doc.customer = details.get("party") target_doc.selling_price_list = source_doc.buying_price_list + update_address(target_doc, 'company_address', 'company_address_display', source_doc.supplier_address) + update_address(target_doc, 'shipping_address_name', 'shipping_address', source_doc.shipping_address) + update_address(target_doc, 'customer_address', 'address_display', source_doc.shipping_address) + if currency: target_doc.currency = currency + update_taxes(target_doc, party=target_doc.customer, party_type='Customer', company=target_doc.company, + doctype=target_doc.doctype, party_address=target_doc.customer_address, + company_address=target_doc.company_address, shipping_address_name=target_doc.shipping_address_name) + item_field_map = { "doctype": target_doctype + " Item", "field_no_map": [ @@ -1597,25 +1620,33 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): "expense_account", "cost_center", "warehouse" - ] + ], + "field_map": { + 'rate': 'rate', + } } - 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' - } + if doctype in ["Sales Invoice", "Sales Order"]: + item_field_map["field_map"].update({ + "name": target_detail_field, }) + if source_doc.get('update_stock'): + item_field_map["field_map"].update({ + 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, "postprocess": update_details, + "set_target_warehouse": "set_from_warehouse", "field_no_map": [ - "taxes_and_charges" + "taxes_and_charges", + "set_warehouse", + "shipping_address" ] }, doctype +" Item": item_field_map @@ -1624,6 +1655,110 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): return doclist +def set_purchase_references(doc): + # add internal PO or PR links if any + if doc.is_internal_transfer(): + if doc.doctype == 'Purchase Receipt': + so_item_map = get_delivery_note_details(doc.inter_company_invoice_reference) + + if so_item_map: + pd_item_map, parent_child_map, warehouse_map = \ + get_pd_details('Purchase Order Item', so_item_map, 'sales_order_item') + + update_pr_items(doc, so_item_map, pd_item_map, parent_child_map, warehouse_map) + + elif doc.doctype == 'Purchase Invoice': + dn_item_map, so_item_map = get_sales_invoice_details(doc.inter_company_invoice_reference) + # First check for Purchase receipt + if list(dn_item_map.values()): + pd_item_map, parent_child_map, warehouse_map = \ + get_pd_details('Purchase Receipt Item', dn_item_map, 'delivery_note_item') + + update_pi_items(doc, 'pr_detail', 'purchase_receipt', + dn_item_map, pd_item_map, parent_child_map, warehouse_map) + + if list(so_item_map.values()): + pd_item_map, parent_child_map, warehouse_map = \ + get_pd_details('Purchase Order Item', so_item_map, 'sales_order_item') + + update_pi_items(doc, 'po_detail', 'purchase_order', + so_item_map, pd_item_map, parent_child_map, warehouse_map) + +def update_pi_items(doc, detail_field, parent_field, sales_item_map, + purchase_item_map, parent_child_map, warehouse_map): + for item in doc.get('items'): + item.set(detail_field, purchase_item_map.get(sales_item_map.get(item.sales_invoice_item))) + item.set(parent_field, parent_child_map.get(sales_item_map.get(item.sales_invoice_item))) + if doc.update_stock: + item.warehouse = warehouse_map.get(sales_item_map.get(item.sales_invoice_item)) + +def update_pr_items(doc, sales_item_map, purchase_item_map, parent_child_map, warehouse_map): + for item in doc.get('items'): + item.purchase_order_item = purchase_item_map.get(sales_item_map.get(item.delivery_note_item)) + item.warehouse = warehouse_map.get(sales_item_map.get(item.delivery_note_item)) + item.purchase_order = parent_child_map.get(sales_item_map.get(item.delivery_note_item)) + +def get_delivery_note_details(internal_reference): + so_item_map = {} + + si_item_details = frappe.get_all('Delivery Note Item', fields=['name', 'so_detail'], + filters={'parent': internal_reference}) + + for d in si_item_details: + so_item_map.setdefault(d.name, d.so_detail) + + return so_item_map + +def get_sales_invoice_details(internal_reference): + dn_item_map = {} + so_item_map = {} + + si_item_details = frappe.get_all('Sales Invoice Item', fields=['name', 'so_detail', + 'dn_detail'], filters={'parent': internal_reference}) + + for d in si_item_details: + if d.dn_detail: + dn_item_map.setdefault(d.name, d.dn_detail) + if d.so_detail: + so_item_map.setdefault(d.name, d.so_detail) + + return dn_item_map, so_item_map + +def get_pd_details(doctype, sd_detail_map, sd_detail_field): + pd_item_map = {} + accepted_warehouse_map = {} + parent_child_map = {} + + pd_item_details = frappe.get_all(doctype, + fields=[sd_detail_field, 'name', 'warehouse', 'parent'], filters={sd_detail_field: ('in', list(sd_detail_map.values()))}) + + for d in pd_item_details: + pd_item_map.setdefault(d.get(sd_detail_field), d.name) + parent_child_map.setdefault(d.get(sd_detail_field), d.parent) + accepted_warehouse_map.setdefault(d.get(sd_detail_field), d.warehouse) + + return pd_item_map, parent_child_map, accepted_warehouse_map + +def update_taxes(doc, party=None, party_type=None, company=None, doctype=None, party_address=None, + company_address=None, shipping_address_name=None, master_doctype=None): + # Update Party Details + party_details = get_party_details(party=party, party_type=party_type, company=company, + doctype=doctype, party_address=party_address, company_address=company_address, + shipping_address=shipping_address_name) + + # Update taxes and charges if any + doc.taxes_and_charges = party_details.get('taxes_and_charges') + doc.set('taxes', party_details.get('taxes')) + +def update_address(doc, address_field, address_display_field, address_name): + doc.set(address_field, address_name) + fetch_values = get_fetch_values(doc.doctype, address_field, address_name) + + for key, value in fetch_values.items(): + doc.set(key, value) + + doc.set(address_display_field, get_address_display(doc.get(address_field))) + @frappe.whitelist() def get_loyalty_programs(customer): ''' sets applicable loyalty program to the customer or returns a list of applicable programs ''' diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 3a6dbeb51c..e94e2cdd95 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -22,6 +22,7 @@ from erpnext.regional.india.utils import get_ewb_data from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice +from erpnext.stock.utils import get_incoming_rate class TestSalesInvoice(unittest.TestCase): def make(self): @@ -688,7 +689,7 @@ class TestSalesInvoice(unittest.TestCase): self.assertTrue(gle) def test_pos_gl_entry_with_perpetual_inventory(self): - make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1", + make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1") pr = make_purchase_receipt(company= "_Test Company with perpetual inventory", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1") @@ -745,7 +746,7 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(pos_return.get('payments')[0].amount, -1000) def test_pos_change_amount(self): - make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1", + make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1") pr = make_purchase_receipt(company= "_Test Company with perpetual inventory", @@ -1770,59 +1771,82 @@ 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") + 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) + 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") + 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") + 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 = 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() + 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() - # 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() + rate = 0.0 + for d in si.get('items'): + rate = get_incoming_rate({ + "item_code": d.item_code, + "warehouse": d.warehouse, + "posting_date": si.posting_date, + "posting_time": si.posting_time, + "qty": -1 * flt(d.get('stock_qty')), + "serial_no": d.serial_no, + "company": si.company, + "voucher_type": 'Sales Invoice', + "voucher_no": si.name, + "allow_zero_valuation": d.get("allow_zero_valuation") + }, raise_error_if_no_rate=False) - # si_gl_entries = [ - # ["_Test Account Excise Duty - TCP1", 0.0, 12.0, nowdate()], - # ["Unrealized Profit - TCP1", 12.0, 0.0, nowdate()] - # ] + rate = flt(rate, 2) - # check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1)) + si.submit() - # pi_gl_entries = [ - # ["_Test Account Excise Duty - TCP1", 12.0 , 0.0, nowdate()], - # ["Unrealized Profit - TCP1", 0.0, 12.0, nowdate()] - # ] + 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() - # check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1)) + tax_amount = flt(rate * (12/100), 2) + si_gl_entries = [ + ["_Test Account Excise Duty - TCP1", 0.0, tax_amount, nowdate()], + ["Unrealized Profit - TCP1", tax_amount, 0.0, nowdate()] + ] + + check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1)) + + pi_gl_entries = [ + ["_Test Account Excise Duty - TCP1", tax_amount , 0.0, nowdate()], + ["Unrealized Profit - TCP1", 0.0, tax_amount, nowdate()] + ] + + # Sale and Purchase both should be at valuation rate + self.assertEqual(si.items[0].rate, rate) + self.assertEqual(target_doc.items[0].rate, rate) + + check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1)) def test_eway_bill_json(self): si = make_sales_invoice_for_ewaybill() diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 3695075798..7a98afff36 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -565,11 +565,12 @@ "print_hide": 1 }, { + "depends_on": "eval: parent.is_internal_customer && parent.update_stock", "fieldname": "target_warehouse", "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, - "label": "Customer Warehouse (Optional)", + "label": "Target Warehouse", "no_copy": 1, "options": "Warehouse", "print_hide": 1 @@ -815,7 +816,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-09-23 19:59:04.879322", + "modified": "2020-12-26 17:25:04.090630", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index eeb5140bbe..cb4d9b43db 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -49,7 +49,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum elif d.po_detail: purchase_receipt = ", ".join(po_pr_map.get(d.po_detail, [])) - expense_account = d.expense_account or aii_account_map.get(d.company) + expense_account = d.unrealized_profit_loss_account or d.expense_account \ + or aii_account_map.get(d.company) row = { 'item_code': d.item_code, @@ -315,6 +316,7 @@ def get_items(filters, additional_query_columns): `tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`, `tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company, `tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total, + `tabPurchase Invoice`.unrealized_profit_loss_account, `tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description, `tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`, `tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`, diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index f54ceb0d2f..998003ac69 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -76,7 +76,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum 'company': d.company, 'sales_order': d.sales_order, 'delivery_note': d.delivery_note, - 'income_account': d.income_account, + 'income_account': d.unrealized_profit_loss_account or d.income_account, 'cost_center': d.cost_center, 'stock_qty': d.stock_qty, 'stock_uom': d.stock_uom @@ -379,6 +379,7 @@ def get_items(filters, additional_query_columns): select `tabSales Invoice Item`.name, `tabSales Invoice Item`.parent, `tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to, + `tabSales Invoice`.unrealized_profit_loss_account, `tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks, `tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total, `tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description, diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index 9399e70739..8ac749d629 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -14,13 +14,15 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum if not filters: filters = {} invoice_list = get_invoices(filters, additional_query_columns) - columns, expense_accounts, tax_accounts = get_columns(invoice_list, additional_table_columns) + columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts \ + = get_columns(invoice_list, additional_table_columns) if not invoice_list: msgprint(_("No record found")) return columns, invoice_list invoice_expense_map = get_invoice_expense_map(invoice_list) + internal_invoice_map = get_internal_invoice_map(invoice_list) invoice_expense_map, invoice_tax_map = get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts) invoice_po_pr_map = get_invoice_po_pr_map(invoice_list) @@ -52,10 +54,17 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum # map expense values base_net_total = 0 for expense_acc in expense_accounts: - expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc)) + if inv.is_internal_supplier and inv.company == inv.represents_company: + expense_amount = 0 + else: + expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc)) base_net_total += expense_amount row.append(expense_amount) + # Add amount in unrealized account + for account in unrealized_profit_loss_accounts: + row.append(flt(internal_invoice_map.get((inv.name, account)))) + # net total row.append(base_net_total or inv.base_net_total) @@ -96,7 +105,8 @@ def get_columns(invoice_list, additional_table_columns): "width": 80 } ] - expense_accounts = tax_accounts = expense_columns = tax_columns = [] + expense_accounts = tax_accounts = expense_columns = tax_columns = unrealized_profit_loss_accounts = \ + unrealized_profit_loss_account_columns = [] if invoice_list: expense_accounts = frappe.db.sql_list("""select distinct expense_account @@ -112,17 +122,25 @@ def get_columns(invoice_list, additional_table_columns): and parent in (%s) order by account_head""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account + from `tabPurchase Invoice` where docstatus = 1 and name in (%s) + and ifnull(unrealized_profit_loss_account, '') != '' + order by unrealized_profit_loss_account""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) expense_columns = [(account + ":Currency/currency:120") for account in expense_accounts] + unrealized_profit_loss_account_columns = [(account + ":Currency/currency:120") for account in unrealized_profit_loss_accounts] + for account in tax_accounts: if account not in expense_accounts: tax_columns.append(account + ":Currency/currency:120") - columns = columns + expense_columns + [_("Net Total") + ":Currency/currency:120"] + tax_columns + \ + columns = columns + expense_columns + unrealized_profit_loss_account_columns + \ + [_("Net Total") + ":Currency/currency:120"] + tax_columns + \ [_("Total Tax") + ":Currency/currency:120", _("Grand Total") + ":Currency/currency:120", _("Rounded Total") + ":Currency/currency:120", _("Outstanding Amount") + ":Currency/currency:120"] - return columns, expense_accounts, tax_accounts + return columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts def get_conditions(filters): conditions = "" @@ -199,6 +217,19 @@ def get_invoice_expense_map(invoice_list): return invoice_expense_map +def get_internal_invoice_map(invoice_list): + unrealized_amount_details = frappe.db.sql("""SELECT name, unrealized_profit_loss_account, + base_net_total as amount from `tabPurchase Invoice` where name in (%s) + and is_internal_supplier = 1 and company = represents_company""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + + internal_invoice_map = {} + for d in unrealized_amount_details: + if d.unrealized_profit_loss_account: + internal_invoice_map.setdefault((d.name, d.unrealized_profit_loss_account), d.amount) + + return internal_invoice_map + def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts): tax_details = frappe.db.sql(""" select parent, account_head, case add_deduct_tax when "Add" then sum(base_tax_amount_after_discount_amount) diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index b6e61b1306..cb2c98b64a 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -15,13 +15,14 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No if not filters: filters = frappe._dict({}) invoice_list = get_invoices(filters, additional_query_columns) - columns, income_accounts, tax_accounts = get_columns(invoice_list, additional_table_columns) + columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns(invoice_list, additional_table_columns) if not invoice_list: msgprint(_("No record found")) return columns, invoice_list invoice_income_map = get_invoice_income_map(invoice_list) + internal_invoice_map = get_internal_invoice_map(invoice_list) invoice_income_map, invoice_tax_map = get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts) #Cost Center & Warehouse Map @@ -70,12 +71,22 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No # map income values base_net_total = 0 for income_acc in income_accounts: - income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc)) + if inv.is_internal_customer and inv.company == inv.represents_company: + income_amount = 0 + else: + income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc)) + base_net_total += income_amount row.update({ frappe.scrub(income_acc): income_amount }) + # Add amount in unrealized account + for account in unrealized_profit_loss_accounts: + row.update({ + frappe.scrub(account): flt(internal_invoice_map.get((inv.name, account))) + }) + # net total row.update({'net_total': base_net_total or inv.base_net_total}) @@ -230,6 +241,8 @@ def get_columns(invoice_list, additional_table_columns): tax_accounts = [] income_columns = [] tax_columns = [] + unrealized_profit_loss_accounts = [] + unrealized_profit_loss_account_columns = [] if invoice_list: income_accounts = frappe.db.sql_list("""select distinct income_account @@ -243,12 +256,18 @@ def get_columns(invoice_list, additional_table_columns): and parent in (%s) order by account_head""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account + from `tabSales Invoice` where docstatus = 1 and name in (%s) + and ifnull(unrealized_profit_loss_account, '') != '' + order by unrealized_profit_loss_account""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + for account in income_accounts: income_columns.append({ "label": account, "fieldname": frappe.scrub(account), "fieldtype": "Currency", - "options": 'currency', + "options": "currency", "width": 120 }) @@ -258,15 +277,24 @@ def get_columns(invoice_list, additional_table_columns): "label": account, "fieldname": frappe.scrub(account), "fieldtype": "Currency", - "options": 'currency', + "options": "currency", "width": 120 }) + for account in unrealized_profit_loss_accounts: + unrealized_profit_loss_account_columns.append({ + "label": account, + "fieldname": frappe.scrub(account), + "fieldtype": "Currency", + "options": "currency", + "width": 120 + }) + net_total_column = [{ "label": _("Net Total"), "fieldname": "net_total", "fieldtype": "Currency", - "options": 'currency', + "options": "currency", "width": 120 }] @@ -301,9 +329,10 @@ def get_columns(invoice_list, additional_table_columns): } ] - columns = columns + income_columns + net_total_column + tax_columns + total_columns + columns = columns + income_columns + unrealized_profit_loss_account_columns + \ + net_total_column + tax_columns + total_columns - return columns, income_accounts, tax_accounts + return columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts def get_conditions(filters): conditions = "" @@ -368,7 +397,8 @@ def get_invoices(filters, additional_query_columns): return frappe.db.sql(""" select name, posting_date, debit_to, project, customer, customer_name, owner, remarks, territory, tax_id, customer_group, - base_net_total, base_grand_total, base_rounded_total, outstanding_amount {0} + base_net_total, base_grand_total, base_rounded_total, outstanding_amount, + is_internal_customer, represents_company, company {0} from `tabSales Invoice` where docstatus = 1 %s order by posting_date desc, name desc""".format(additional_query_columns or '') % conditions, filters, as_dict=1) @@ -385,6 +415,19 @@ def get_invoice_income_map(invoice_list): return invoice_income_map +def get_internal_invoice_map(invoice_list): + unrealized_amount_details = frappe.db.sql("""SELECT name, unrealized_profit_loss_account, + base_net_total as amount from `tabSales Invoice` where name in (%s) + and is_internal_customer = 1 and company = represents_company""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + + internal_invoice_map = {} + for d in unrealized_amount_details: + if d.unrealized_profit_loss_account: + internal_invoice_map.setdefault((d.name, d.unrealized_profit_loss_account), d.amount) + + return internal_invoice_map + def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts): tax_details = frappe.db.sql("""select parent, account_head, sum(base_tax_amount_after_discount_amount) as tax_amount diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 83efa66f6b..dd0f065848 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -164,16 +164,16 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( if (doc.docstatus === 1 && !doc.inter_company_order_reference) { let me = this; - frappe.model.with_doc("Supplier", me.frm.doc.supplier, () => { - let supplier = frappe.model.get_doc("Supplier", me.frm.doc.supplier); - let internal = supplier.is_internal_supplier; - let disabled = supplier.disabled; - if (internal === 1 && disabled === 0) { - me.frm.add_custom_button("Inter Company Order", function() { - me.make_inter_company_order(me.frm); - }, __('Create')); - } - }); + let internal = me.frm.doc.is_internal_supplier; + if (internal) { + let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Sales Order" : + "Inter Company Sales Order"; + + me.frm.add_custom_button(button_label, function() { + me.make_inter_company_order(me.frm); + }, __('Create')); + } + } } diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 75da71ceff..ee2beea67f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -134,6 +134,8 @@ "ref_sq", "column_break_74", "party_account_currency", + "is_internal_supplier", + "represents_company", "inter_company_order_reference" ], "fields": [ @@ -1101,13 +1103,28 @@ { "fieldname": "items_col_break", "fieldtype": "Column Break" + }, + { + "default": "0", + "fetch_from": "supplier.is_internal_supplier", + "fieldname": "is_internal_supplier", + "fieldtype": "Check", + "label": "Is Internal Supplier" + }, + { + "fetch_from": "supplier.represents_company", + "fieldname": "represents_company", + "fieldtype": "Link", + "label": "Represents Company", + "options": "Company", + "read_only": 1 } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-12-03 16:46:44.229351", + "modified": "2021-01-20 22:07:23.487138", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index 0ee9d180d9..edeb135d95 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -52,7 +52,10 @@ class Supplier(TransactionBase): 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"): + internal_supplier = frappe.db.get_value("Supplier", + {"is_internal_supplier": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name") + + if internal_supplier: frappe.throw(_("Internal Supplier for company {0} already exists").format( frappe.bold(self.represents_company))) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index b9bfbaf0bd..eaa80800c6 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -75,6 +75,9 @@ class AccountsController(TransactionBase): self.ensure_supplier_is_not_blocked() self.validate_date_with_fiscal_year() + self.validate_inter_company_reference() + + self.set_incoming_rate() if self.meta.get_field("currency"): self.calculate_taxes_and_totals() @@ -110,12 +113,12 @@ class AccountsController(TransactionBase): self.set_inter_company_account() validate_regional(self) - + validate_einvoice_fields(self) if self.doctype != 'Material Request': apply_pricing_rule_on_transaction(self) - + def before_cancel(self): validate_einvoice_fields(self) @@ -212,6 +215,17 @@ class AccountsController(TransactionBase): validate_fiscal_year(self.get(date_field), self.fiscal_year, self.company, self.meta.get_label(date_field), self) + def validate_inter_company_reference(self): + if self.doctype not in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'): + return + + if self.is_internal_transfer(): + if not (self.get('inter_company_reference') or self.get('inter_company_invoice_reference') + or self.get('inter_company_order_reference')): + msg = _("Internal Sale or Delivery Reference missing. ") + msg += _("Please create purchase from internal sale or delivery document itself") + frappe.throw(msg, title=_("Internal Sales Reference Missing")) + def validate_due_date(self): if self.get('is_pos'): return @@ -968,9 +982,9 @@ class AccountsController(TransactionBase): It will an internal transfer if its an internal customer and representation company is same as billing company """ - if self.doctype == 'Sales Invoice': + if self.doctype in ('Sales Invoice', 'Delivery Note', 'Sales Order'): internal_party_field = 'is_internal_customer' - else: + elif self.doctype in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'): internal_party_field = 'is_internal_supplier' if self.get(internal_party_field) and (self.represents_company == self.company): diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 4dee375e5a..ab1f02779b 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -44,7 +44,6 @@ 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() @@ -100,11 +99,6 @@ class BuyingController(StockController): 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"]] @@ -224,6 +218,48 @@ class BuyingController(StockController): else: item.valuation_rate = 0.0 + def set_incoming_rate(self): + if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Purchase Order"): + return + + ref_doctype_map = { + "Purchase Order": "Sales Order Item", + "Purchase Receipt": "Delivery Note Item", + "Purchase Invoice": "Sales Invoice Item", + } + + ref_doctype = ref_doctype_map.get(self.doctype) + items = self.get("items") + for d in items: + if not cint(self.get("is_return")): + # Get outgoing rate based on original item cost based on valuation method + + if not d.get(frappe.scrub(ref_doctype)): + outgoing_rate = get_incoming_rate({ + "item_code": d.item_code, + "warehouse": d.get('from_warehouse'), + "posting_date": self.get('posting_date') or self.get('transation_date'), + "posting_time": self.get('posting_time'), + "qty": -1 * flt(d.get('stock_qty')), + "serial_no": d.get('serial_no'), + "company": self.company, + "voucher_type": self.doctype, + "voucher_no": self.name, + "allow_zero_valuation": d.get("allow_zero_valuation") + }, raise_error_if_no_rate=False) + + rate = flt(outgoing_rate * d.conversion_factor, d.precision('rate')) + else: + rate = frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), 'rate') + + if self.is_internal_transfer(): + if rate != d.rate: + d.rate = rate + d.discount_percentage = 0 + d.discount_amount = 0 + frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer") + .format(d.idx), alert=1) + def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True): supplied_items_cost = 0.0 for d in self.get("supplied_items"): @@ -243,7 +279,7 @@ class BuyingController(StockController): d.amount = flt(flt(d.consumed_qty) * flt(d.rate), d.precision("amount")) supplied_items_cost += flt(d.amount) - + return supplied_items_cost def validate_for_subcontracting(self): @@ -559,6 +595,8 @@ class BuyingController(StockController): from_warehouse_sle = self.get_sl_entries(d, { "actual_qty": -1 * pr_qty, "warehouse": d.from_warehouse, + "outgoing_rate": d.rate, + "recalculate_rate": 1, "dependant_sle_voucher_detail_no": d.name }) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 812021f5c8..e085048f99 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import cint, flt, cstr, comma_or, get_link_to_form +from frappe.utils import cint, flt, cstr, get_link_to_form, nowtime from frappe import _, throw from erpnext.stock.get_item_details import get_bin_details from erpnext.stock.utils import get_incoming_rate @@ -49,7 +49,6 @@ class SellingController(StockController): self.set_customer_address() self.validate_for_duplicate_items() self.validate_target_warehouse() - self.set_incoming_rate() def set_missing_values(self, for_validate=False): @@ -191,7 +190,7 @@ class SellingController(StockController): for it in self.get("items"): if not it.item_code: continue - + last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"]) last_purchase_rate_in_sales_uom = last_purchase_rate * (it.conversion_factor or 1) if flt(it.base_net_rate) < flt(last_purchase_rate_in_sales_uom): @@ -312,7 +311,7 @@ class SellingController(StockController): sales_order.update_reserved_qty(so_item_rows) def set_incoming_rate(self): - if self.doctype not in ("Delivery Note", "Sales Invoice"): + if self.doctype not in ("Delivery Note", "Sales Invoice", "Sales Order"): return items = self.get("items") + (self.get("packed_items") or []) @@ -322,15 +321,26 @@ class SellingController(StockController): d.incoming_rate = get_incoming_rate({ "item_code": d.item_code, "warehouse": d.warehouse, - "posting_date": self.posting_date, - "posting_time": self.posting_time, - "qty": -1*flt(d.qty), - "serial_no": d.serial_no, + "posting_date": self.get('posting_date') or self.get('transaction_date'), + "posting_time": self.get('posting_time') or nowtime(), + "qty": -1 * flt(d.get('stock_qty') or d.get('actual_qty')), + "serial_no": d.get('serial_no'), "company": self.company, "voucher_type": self.doctype, "voucher_no": self.name, "allow_zero_valuation": d.get("allow_zero_valuation") }, raise_error_if_no_rate=False) + + # For internal transfers use incoming rate as the valuation rate + if self.is_internal_transfer(): + rate = flt(d.incoming_rate * d.conversion_factor, d.precision('rate')) + if d.rate != rate: + d.rate = rate + d.discount_percentage = 0 + d.discount_amount = 0 + frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer") + .format(d.idx), alert=1) + elif self.get("return_against"): # Get incoming rate of return entry from reference document # based on original item cost as per valuation method @@ -391,7 +401,7 @@ class SellingController(StockController): }) if item_row.warehouse: sle.dependant_sle_voucher_detail_no = item_row.name - + return sle def set_po_nos(self, for_validate=False): diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index e0fcf47365..4b5e347970 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -24,6 +24,7 @@ class StockController(AccountsController): self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() + self.validate_internal_transfer() self.validate_putaway_capacity() def make_gl_entries(self, gl_entries=None, from_repost=False): @@ -74,6 +75,7 @@ class StockController(AccountsController): warehouse_with_no_account = [] precision = frappe.get_precision("GL Entry", "debit_in_account_currency") for item_row in voucher_details: + sle_list = sle_map.get(item_row.name) if sle_list: for sle in sle_list: @@ -218,7 +220,7 @@ class StockController(AccountsController): """, (self.doctype, self.name), as_dict=True) for sle in stock_ledger_entries: - stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle) + stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle) return stock_ledger def make_batches(self, warehouse_field): @@ -393,6 +395,32 @@ class StockController(AccountsController): if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): d.allow_zero_valuation_rate = 1 + def validate_internal_transfer(self): + if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \ + and self.is_internal_transfer(): + self.validate_in_transit_warehouses() + self.validate_multi_currency() + self.validate_packed_items() + + def validate_in_transit_warehouses(self): + if (self.doctype == 'Sales Invoice' and self.get('update_stock')) or self.doctype == 'Delivery Note': + for item in self.get('items'): + if not item.target_warehouse: + frappe.throw(_("Row {0}: Target Warehouse is mandatory for internal transfers").format(item.idx)) + + if (self.doctype == 'Purchase Invoice' and self.get('update_stock')) or self.doctype == 'Purchase Receipt': + for item in self.get('items'): + if not item.from_warehouse: + frappe.throw(_("Row {0}: From Warehouse is mandatory for internal transfers").format(item.idx)) + + def validate_multi_currency(self): + if self.currency != self.company_currency: + frappe.throw(_("Internal transfers can only be done in company's default currency")) + + def validate_packed_items(self): + if self.doctype in ('Sales Invoice', 'Delivery Note Item') and self.get('packed_items'): + frappe.throw(_("Packed Items cannot be transferred internally")) + def validate_putaway_capacity(self): # if over receipt is attempted while 'apply putaway rule' is disabled # and if rule was applied on the transaction, validate it. diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 1d1f18b1a0..9627600a17 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -105,10 +105,18 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ frappe.ui.form.on(this.frm.doctype + " Item", { items_add: function(frm, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); - if(!item.warehouse && frm.doc.set_warehouse) { + if (!item.warehouse && frm.doc.set_warehouse) { item.warehouse = frm.doc.set_warehouse; } + if (!item.target_warehouse && frm.doc.set_target_warehouse) { + item.target_warehouse = frm.doc.set_target_warehouse; + } + + if (!item.from_warehouse && frm.doc.set_from_warehouse) { + item.from_warehouse = frm.doc.set_from_warehouse; + } + erpnext.accounts.dimensions.copy_dimension_from_first_row(frm, cdt, cdn, 'items'); } }); @@ -227,6 +235,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }; + this.frm.trigger('set_default_internal_warehouse'); + return frappe.run_serially([ () => set_value('currency', currency), () => set_value('price_list_currency', currency), @@ -658,7 +668,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ args: item_args }, callback: function(r) { - frappe.model.set_value(item.doctype, item.name, 'rate', r.message); + frappe.model.set_value(item.doctype, item.name, 'rate', r.message * item.conversion_factor); } }); }, @@ -724,6 +734,31 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ this.calculate_taxes_and_totals(false); }, + update_stock: function() { + this.frm.trigger('set_default_internal_warehouse'); + }, + + set_default_internal_warehouse: function() { + let me = this; + if ((this.frm.doc.doctype === 'Sales Invoice' && me.frm.doc.update_stock) + || this.frm.doc.doctype == 'Delivery Note') { + if (this.frm.doc.is_internal_customer && this.frm.doc.company === this.frm.doc.represents_company) { + frappe.db.get_value('Company', this.frm.doc.company, 'default_in_transit_warehouse', function(value) { + me.frm.set_value('set_target_warehouse', value.default_in_transit_warehouse); + }); + } + } + + if ((this.frm.doc.doctype === 'Purchase Invoice' && me.frm.doc.update_stock) + || this.frm.doc.doctype == 'Purchase Receipt') { + if (this.frm.doc.is_internal_supplier && this.frm.doc.company === this.frm.doc.represents_company) { + frappe.db.get_value('Company', this.frm.doc.company, 'default_in_transit_warehouse', function(value) { + me.frm.set_value('set_from_warehouse', value.default_in_transit_warehouse); + }); + } + } + }, + company: function() { var me = this; var set_pricing = function() { @@ -810,7 +845,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) { erpnext.utils.get_shipping_address(this.frm, function(){ set_party_account(set_pricing); - }) + }); // Get default company billing address in Purchase Invoice, Order and Receipt frappe.call({ @@ -1977,6 +2012,14 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ this.autofill_warehouse(this.frm.doc.items, "warehouse", this.frm.doc.set_warehouse); }, + set_target_warehouse: function() { + this.autofill_warehouse(this.frm.doc.items, "target_warehouse", this.frm.doc.set_target_warehouse); + }, + + set_from_warehouse: function() { + this.autofill_warehouse(this.frm.doc.items, "from_warehouse", this.frm.doc.set_from_warehouse); + }, + autofill_warehouse : function (child_table, warehouse_field, warehouse) { if (warehouse && child_table && child_table.length) { let doctype = child_table[0].doctype; diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index 770704e595..808dd5add0 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -276,6 +276,12 @@ erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) { erpnext.utils.get_shipping_address = function(frm, callback){ if (frm.doc.company) { + if (!(frm.doc.inter_com_order_reference || frm.doc.internal_invoice_reference || + frm.doc.internal_order_reference)) { + if (callback) { + return callback(); + } + } frappe.call({ method: "erpnext.accounts.custom.address.get_shipping_address", args: { diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js index 87baece65d..f09d3d08ad 100644 --- a/erpnext/regional/india/taxes.js +++ b/erpnext/regional/india/taxes.js @@ -40,14 +40,12 @@ 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('taxes', r.message.taxes); 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 0d8263835d..e89885f380 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -171,7 +171,7 @@ def get_regional_address_details(party_details, doctype, company): if is_internal_transfer(party_details, doctype): party_details.taxes_and_charges = '' - party_details.taxes = '' + party_details.taxes = [] return party_details if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 29214ee06d..bf8b7fc128 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -84,7 +84,10 @@ class Customer(TransactionBase): 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"): + internal_customer = frappe.db.get_value("Customer", + {"is_internal_customer": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name") + + if internal_customer: frappe.throw(_("Internal Customer for company {0} already exists").format( frappe.bold(self.represents_company))) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index d4fb07cc27..78f9df9588 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -171,8 +171,10 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( this.frm.add_custom_button(__('Request for Raw Materials'), () => this.make_raw_material_request(), __('Create')); } - // make purchase order + // Make Purchase Order + if (!this.frm.doc.is_internal_customer) { this.frm.add_custom_button(__('Purchase Order'), () => this.make_purchase_order(), __('Create')); + } // maintenance if(flt(doc.per_delivered, 2) < 100 && (order_is_maintenance || order_is_a_custom_sale)) { @@ -193,16 +195,15 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( if (doc.docstatus === 1 && !doc.inter_company_order_reference) { let me = this; - frappe.model.with_doc("Customer", me.frm.doc.customer, () => { - let customer = frappe.model.get_doc("Customer", me.frm.doc.customer); - let internal = customer.is_internal_customer; - let disabled = customer.disabled; - if (internal === 1 && disabled === 0) { - me.frm.add_custom_button("Inter Company Order", function() { - me.make_inter_company_order(); - }, __('Create')); - } - }); + let internal = me.frm.doc.is_internal_customer; + if (internal) { + let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Order" : + "Inter Company Purchase Order"; + + me.frm.add_custom_button(button_label, function() { + me.make_inter_company_order(); + }, __('Create')); + } } } // payment request diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 3d64ac3780..0a5c6651ba 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -107,6 +107,8 @@ "tc_name", "terms", "more_info", + "is_internal_customer", + "represents_company", "inter_company_order_reference", "project", "party_account_currency", @@ -1103,7 +1105,8 @@ "hide_days": 1, "hide_seconds": 1, "label": "Inter Company Order Reference", - "options": "Purchase Order" + "options": "Purchase Order", + "read_only": 1 }, { "description": "Track this Sales Order against any Project", @@ -1455,13 +1458,29 @@ "hide_seconds": 1, "label": "Skip Delivery Note", "print_hide": 1 + }, + { + "default": "0", + "fetch_from": "customer.is_internal_customer", + "fieldname": "is_internal_customer", + "fieldtype": "Check", + "label": "Is Internal Customer", + "read_only": 1 + }, + { + "fetch_from": "customer.represents_company", + "fieldname": "represents_company", + "fieldtype": "Link", + "label": "Represents Company", + "options": "Company", + "read_only": 1 } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-10-30 13:59:18.628077", + "modified": "2021-01-20 23:40:39.929296", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index ee18042fdd..334bdeac9d 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -95,13 +95,19 @@ frappe.ui.form.on("Delivery Note", { frm.page.set_inner_btn_group_as_primary(__('Create')); } - if (frm.doc.docstatus === 1 && frm.doc.is_internal_customer && !frm.doc.inter_company_reference) { - frm.add_custom_button(__('Purchase Receipt'), function() { - frappe.model.open_mapped_doc({ - method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt', - frm: frm, - }) - }, __('Create')); + if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) { + let internal = me.frm.doc.is_internal_customer; + if (internal) { + let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Receipt" : + "Inter Company Purchase Receipt"; + + me.frm.add_custom_button(button_label, function() { + frappe.model.open_mapped_doc({ + method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt', + frm: frm, + }); + }, __('Create')); + } } } }); @@ -297,15 +303,6 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend( } }) }, - - to_warehouse: function() { - let packed_items_table = this.frm.doc["packed_items"]; - this.autofill_warehouse(this.frm.doc["items"], "target_warehouse", this.frm.doc.to_warehouse); - if (packed_items_table && packed_items_table.length) { - this.autofill_warehouse(packed_items_table, "target_warehouse", this.frm.doc.to_warehouse); - } - } - }); $.extend(cur_frm.cscript, new erpnext.stock.DeliveryNoteController({frm: cur_frm})); diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index c9f8d0810e..f595aade91 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -53,7 +53,7 @@ "sec_warehouse", "set_warehouse", "col_break_warehouse", - "to_warehouse", + "set_target_warehouse", "items_section", "scan_barcode", "items", @@ -117,6 +117,7 @@ "source", "column_break5", "is_internal_customer", + "represents_company", "inter_company_reference", "per_billed", "customer_group", @@ -502,18 +503,6 @@ "fieldname": "col_break_warehouse", "fieldtype": "Column Break" }, - { - "description": "Required only for sample item.", - "fieldname": "to_warehouse", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "To Warehouse", - "no_copy": 1, - "oldfieldname": "to_warehouse", - "oldfieldtype": "Link", - "options": "Warehouse", - "print_hide": 1 - }, { "fieldname": "items_section", "fieldtype": "Section Break", @@ -1261,13 +1250,34 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "depends_on": "eval: doc.is_internal_customer", + "fieldname": "set_target_warehouse", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Set Target Warehouse", + "no_copy": 1, + "oldfieldname": "to_warehouse", + "oldfieldtype": "Link", + "options": "Warehouse", + "print_hide": 1 + }, + { + "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-truck", "idx": 146, "is_submittable": 1, "links": [], - "modified": "2020-11-30 12:54:45.407289", + "modified": "2020-12-26 17:07:59.194403", "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 a30cadf0a0..fa5a7fbe71 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -664,7 +664,8 @@ def make_inter_company_purchase_receipt(source_name, target_doc=None): return make_inter_company_transaction("Delivery Note", source_name, target_doc) def make_inter_company_transaction(doctype, source_name, target_doc=None): - from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_transaction, get_inter_company_details + from erpnext.accounts.doctype.sales_invoice.sales_invoice import (validate_inter_company_transaction, + get_inter_company_details, update_address, update_taxes, set_purchase_references) if doctype == 'Delivery Note': source_doc = frappe.get_doc(doctype, source_name) @@ -682,6 +683,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): def set_missing_values(source, target): target.run_method("set_missing_values") + set_purchase_references(target) if target.doctype == 'Purchase Receipt': master_doctype = 'Purchase Taxes and Charges Template' @@ -697,21 +699,35 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): if target_doc.doctype == 'Purchase Receipt': target_doc.company = details.get("company") target_doc.supplier = details.get("party") - target_doc.supplier_address = source_doc.company_address - target_doc.shipping_address = source_doc.shipping_address_name or source_doc.customer_address target_doc.buying_price_list = source_doc.selling_price_list target_doc.is_internal_supplier = 1 target_doc.inter_company_reference = source_doc.name + + # Invert the address on target doc creation + update_address(target_doc, 'supplier_address', 'address_display', source_doc.company_address) + update_address(target_doc, 'shipping_address', 'shipping_address_display', source_doc.customer_address) + + update_taxes(target_doc, party=target_doc.supplier, party_type='Supplier', company=target_doc.company, + doctype=target_doc.doctype, party_address=target_doc.supplier_address, + company_address=target_doc.shipping_address) else: target_doc.company = details.get("company") target_doc.customer = details.get("party") target_doc.company_address = source_doc.supplier_address - target_doc.shipping_address_name = source_doc.shipping_address target_doc.selling_price_list = source_doc.buying_price_list target_doc.is_internal_customer = 1 target_doc.inter_company_reference = source_doc.name - doclist = get_mapped_doc(doctype, source_name, { + # Invert the address on target doc creation + update_address(target_doc, 'company_address', 'company_address_display', source_doc.supplier_address) + update_address(target_doc, 'shipping_address_name', 'shipping_address', source_doc.shipping_address) + update_address(target_doc, 'customer_address', 'address_display', source_doc.shipping_address) + + update_taxes(target_doc, party=target_doc.customer, party_type='Customer', company=target_doc.company, + doctype=target_doc.doctype, party_address=target_doc.customer_address, + company_address=target_doc.company_address, shipping_address_name=target_doc.shipping_address_name) + + doclist = get_mapped_doc(doctype, source_name, { doctype: { "doctype": target_doctype, "postprocess": update_details, @@ -722,7 +738,10 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): doctype +" Item": { "doctype": target_doctype + " Item", "field_map": { - source_document_warehouse_field: target_document_warehouse_field + source_document_warehouse_field: target_document_warehouse_field, + 'name': 'delivery_note_item', + 'batch_no': 'batch_no', + 'serial_no': 'serial_no' }, "field_no_map": [ "warehouse" 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 4bbf3de594..9de088df0e 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -458,7 +458,7 @@ "fieldname": "warehouse", "fieldtype": "Link", "in_list_view": 1, - "label": "From Warehouse", + "label": "Warehouse", "oldfieldname": "warehouse", "oldfieldtype": "Link", "options": "Warehouse", @@ -467,11 +467,12 @@ "width": "100px" }, { + "depends_on": "eval:parent.is_internal_customer", "fieldname": "target_warehouse", "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, - "label": "Customer Warehouse (Optional)", + "label": "Target Warehouse", "no_copy": 1, "options": "Warehouse", "print_hide": 1 @@ -748,7 +749,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-12-07 19:59:27.119856", + "modified": "2020-12-26 17:31:27.029803", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 55f0f0cb26..32d349f303 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -48,6 +48,7 @@ "set_warehouse", "rejected_warehouse", "col_break_warehouse", + "set_from_warehouse", "is_subcontracted", "supplier_warehouse", "items_section", @@ -115,6 +116,7 @@ "per_returned", "is_internal_supplier", "inter_company_reference", + "represents_company", "subscription_detail", "auto_repeat", "printing_settings", @@ -1087,7 +1089,9 @@ "fieldname": "inter_company_reference", "fieldtype": "Link", "label": "Inter Company Reference", + "no_copy": 1, "options": "Delivery Note", + "print_hide": 1, "read_only": 1 }, { @@ -1121,13 +1125,29 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "depends_on": "eval: doc.is_internal_supplier", + "description": "Sets 'From Warehouse' in each row of the items table.", + "fieldname": "set_from_warehouse", + "fieldtype": "Link", + "label": "Set From Warehouse", + "options": "Warehouse" + }, + { + "fetch_from": "supplier.represents_company", + "fieldname": "represents_company", + "fieldtype": "Link", + "label": "Represents Company", + "options": "Company", + "read_only": 1 } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-12-08 18:31:32.234503", + "modified": "2020-12-26 20:49:39.106049", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", 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 662e50c693..e99119202e 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -76,6 +76,7 @@ "purchase_order_item", "material_request_item", "purchase_receipt_item", + "delivery_note_item", "putaway_rule", "section_break_45", "allow_zero_valuation_rate", @@ -819,11 +820,12 @@ "read_only": 1 }, { + "depends_on": "eval:parent.is_internal_supplier", "fieldname": "from_warehouse", "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, - "label": "Supplier Warehouse", + "label": "From Warehouse", "options": "Warehouse" }, { @@ -871,12 +873,20 @@ "fieldtype": "Float", "label": "Received Qty in Stock UOM", "print_hide": 1 + }, + { + "fieldname": "delivery_note_item", + "fieldtype": "Data", + "label": "Delivery Note Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-09 10:00:38.204294", + "modified": "2020-12-26 16:50:56.479347", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 5b9ada0ee5..2b2a7a202d 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -41,7 +41,7 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc if sle.get("actual_qty") or sle.get("voucher_type")=="Stock Reconciliation": sle_doc = make_entry(sle, allow_negative_stock, via_landed_cost_voucher) - + args = sle_doc.as_dict() update_bin(args, allow_negative_stock, via_landed_cost_voucher) @@ -65,7 +65,7 @@ def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False): def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=False, via_landed_cost_voucher=False): if not args and voucher_type and voucher_no: args = get_args_for_voucher(voucher_type, voucher_no) - + distinct_item_warehouses = [(d.item_code, d.warehouse) for d in args] i = 0 @@ -80,7 +80,7 @@ def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negat for item_wh, new_sle in iteritems(obj.new_items): if item_wh not in distinct_item_warehouses: args.append(new_sle) - + i += 1 def get_args_for_voucher(voucher_type, voucher_no): @@ -127,7 +127,7 @@ class update_entries_after(object): self.initialize_previous_data(self.args) self.build() - + def get_precision(self): company_base_currency = frappe.get_cached_value('Company', self.company, "default_currency") self.precision = get_field_precision(frappe.get_meta("Stock Ledger Entry").get_field("stock_value"), @@ -213,13 +213,13 @@ class update_entries_after(object): # includes current entry! args = self.data[self.args.warehouse].previous_sle \ or frappe._dict({"item_code": self.item_code, "warehouse": self.args.warehouse}) - + return list(self.get_sle_after_datetime(args)) def get_dependent_entries_to_fix(self, entries_to_fix, sle): dependant_sle = get_sle_by_voucher_detail_no(sle.dependant_sle_voucher_detail_no, excluded_sle=sle.name) - + if not dependant_sle: return elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse: @@ -251,7 +251,7 @@ class update_entries_after(object): # Get dynamic incoming/outgoing rate self.get_dynamic_incoming_outgoing_rate(sle) - + if sle.serial_no: self.get_serialized_values(sle) self.wh_data.qty_after_transaction += flt(sle.actual_qty) @@ -329,7 +329,7 @@ class update_entries_after(object): rate = get_rate_for_return(sle.voucher_type, sle.voucher_no, sle.item_code, voucher_detail_no=sle.voucher_detail_no) else: if sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"): - rate_field = "valuation_rate" + rate_field = "valuation_rate" else: rate_field = "incoming_rate" @@ -344,7 +344,7 @@ class update_entries_after(object): ref_doctype = "Packed Item" else: ref_doctype = "Purchase Receipt Item Supplied" - + rate = frappe.db.get_value(ref_doctype, {"parent_detail_docname": sle.voucher_detail_no, "item_code": sle.item_code}, rate_field) @@ -374,7 +374,7 @@ class update_entries_after(object): stock_entry.db_update() for d in stock_entry.items: d.db_update() - + def update_rate_on_delivery_and_sales_return(self, sle, outgoing_rate): # Update item's incoming rate on transaction item_code = frappe.db.get_value(sle.voucher_type + " Item", sle.voucher_detail_no, "item_code") @@ -487,7 +487,6 @@ class update_entries_after(object): self.wh_data.valuation_rate = new_stock_value / new_stock_qty else: self.wh_data.valuation_rate = sle.outgoing_rate - else: if flt(self.wh_data.qty_after_transaction) >= 0 and sle.outgoing_rate: self.wh_data.valuation_rate = sle.outgoing_rate @@ -631,7 +630,7 @@ class update_entries_after(object): frappe.throw(message, NegativeStockError, title='Insufficient Stock') else: raise NegativeStockError(message) - + def update_bin(self): # update bin for each warehouse for warehouse, data in iteritems(self.data): @@ -766,7 +765,7 @@ def update_qty_in_future_sle(args, allow_negative_stock=None): frappe.db.sql(""" update `tabStock Ledger Entry` set qty_after_transaction = qty_after_transaction + {qty} - where + where item_code = %(item_code)s and warehouse = %(warehouse)s and voucher_no != %(voucher_no)s @@ -794,7 +793,7 @@ def validate_negative_qty_in_future_sle(args, allow_negative_stock=None): frappe.get_desk_link('Warehouse', args.warehouse), sle[0]["posting_date"], sle[0]["posting_time"], frappe.get_desk_link(sle[0]["voucher_type"], sle[0]["voucher_no"])) - + frappe.throw(message, NegativeStockError, title='Insufficient Stock') def get_future_sle_with_negative_qty(args): @@ -803,7 +802,7 @@ def get_future_sle_with_negative_qty(args): qty_after_transaction, posting_date, posting_time, voucher_type, voucher_no from `tabStock Ledger Entry` - where + where item_code = %(item_code)s and warehouse = %(warehouse)s and voucher_no != %(voucher_no)s From e6f925e5e6f121ce4726d2e8d2ad6aab92723430 Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 28 Jan 2021 15:58:03 +0530 Subject: [PATCH 586/728] review changes --- erpnext/templates/pages/cart.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/templates/pages/cart.html b/erpnext/templates/pages/cart.html index a30969825b..876eaea8e6 100644 --- a/erpnext/templates/pages/cart.html +++ b/erpnext/templates/pages/cart.html @@ -48,7 +48,7 @@ {% if doc.items %}
    - {{ _("Add More") }} + {{ _("Continue Shopping") }} {% if cart_settings.enable_checkout %}
    `; } else { return `
    ${frappe.get_abbr(item.item_name)}
    `; } @@ -105,7 +107,7 @@ erpnext.PointOfSale.ItemSelector = class {
    ${format_currency(item.price_list_rate, item.currency, 0) || 0}
    ` - ) + ); } make_search_bar() { @@ -140,7 +142,7 @@ erpnext.PointOfSale.ItemSelector = class { filters: { pos_profile: doc ? doc.pos_profile : '' } - } + }; }, }, parent: this.$component.find('.item-group-field'), @@ -176,7 +178,7 @@ erpnext.PointOfSale.ItemSelector = class { uom = uom === "undefined" ? undefined : uom; me.events.item_selected({ field: 'qty', value: "+1", item: { item_code, batch_no, serial_no, uom }}); - }) + }); this.search_field.$input.on('input', (e) => { clearTimeout(this.last_search); @@ -247,6 +249,7 @@ erpnext.PointOfSale.ItemSelector = class { this.get_items({ search_value: search_term }) .then(({ message }) => { + // eslint-disable-next-line no-unused-vars const { items, serial_no, batch_no, barcode } = message; if (search_term && !barcode) { this.search_index[search_term] = items; @@ -260,26 +263,26 @@ erpnext.PointOfSale.ItemSelector = class { add_filtered_item_to_cart() { this.$items_container.find(".item-wrapper").click(); } - + 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(12, 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)'); + 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') + this.$component.css('grid-column', 'span 2 / span 2') : + this.$component.css('grid-column', 'span 6 / span 6'); minimize ? - this.$items_container.css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') : - this.$items_container.css('grid-template-columns', 'repeat(4, minmax(0, 1fr))') + 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) { show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none'); } -} \ No newline at end of file +}; \ 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 79eaf05cb2..be2b769a8a 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 @@ -48,7 +48,7 @@ erpnext.PointOfSale.PastOrderSummary = class { const email_dialog = new frappe.ui.Dialog({ title: 'Email Receipt', fields: [ - {fieldname:'email_id', fieldtype:'Data', options: 'Email', label:'Email ID'}, + {fieldname: 'email_id', fieldtype: 'Data', options: 'Email', label: 'Email ID'}, // {fieldname:'remarks', fieldtype:'Text', label:'Remarks (if any)'} ], primary_action: () => { @@ -61,7 +61,7 @@ erpnext.PointOfSale.PastOrderSummary = class { const print_dialog = new frappe.ui.Dialog({ title: 'Print Receipt', fields: [ - {fieldname:'print', fieldtype:'Data', label:'Print Preview'} + {fieldname: 'print', fieldtype: 'Data', label: 'Print Preview'} ], primary_action: () => { const frm = this.events.get_frm(); @@ -132,18 +132,17 @@ erpnext.PointOfSale.PastOrderSummary = class { get_taxes_html(doc) { if (!doc.taxes.length) return ''; - return ` -
    - ${ - 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('') - } -
    `; + let taxes_html = doc.taxes.map(t => { + 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(''); + + return `
    ${taxes_html}
    `; } get_grand_total_html(doc) { @@ -178,9 +177,6 @@ erpnext.PointOfSale.PastOrderSummary = class { this.$summary_container.on('click', '.delete-btn', () => { this.events.delete_order(this.doc.name); this.show_summary_placeholder(); - // this.toggle_component(false); - // this.$component.find('.no-summary-placeholder').removeClass('d-none'); - // this.$summary_wrapper.addClass('d-none'); }); this.$summary_container.on('click', '.new-btn', () => { @@ -237,7 +233,7 @@ erpnext.PointOfSale.PastOrderSummary = class { const print_format = frm.pos_print_format; frappe.call({ - method:"frappe.core.doctype.communication.email.make", + method: "frappe.core.doctype.communication.email.make", args: { recipients: recipients, subject: __(frm.meta.name) + ': ' + doc.name, @@ -246,14 +242,14 @@ erpnext.PointOfSale.PastOrderSummary = class { send_email: 1, print_format, sender_full_name: frappe.user.full_name(), - _lang : doc.language + _lang: doc.language }, callback: r => { - if(!r.exc) { + if (!r.exc) { frappe.utils.play_sound("email"); - if(r.message["emails_not_sent_to"]) { + if (r.message["emails_not_sent_to"]) { frappe.msgprint(__( - "Email not sent to {0} (unsubscribed / disabled)", + "Email not sent to {0} (unsubscribed / disabled)", [ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ] )); } else { @@ -309,10 +305,10 @@ erpnext.PointOfSale.PastOrderSummary = class { load_summary_of(doc, after_submission=false) { after_submission ? this.$component.css('grid-column', 'span 10 / span 10') : - this.$component.css('grid-column', 'span 6 / span 6') - - this.toggle_summary_placeholder(false) - + this.$component.css('grid-column', 'span 6 / span 6'); + + this.toggle_summary_placeholder(false); + this.doc = doc; this.attach_document_info(doc); @@ -338,7 +334,7 @@ erpnext.PointOfSale.PastOrderSummary = class { attach_items_info(doc) { this.$items_container.html(''); - doc.items.forEach((item, i) => { + doc.items.forEach(item => { const item_dom = this.get_item_html(doc, item); this.$items_container.append(item_dom); this.set_dynamic_rate_header_width(); From a3585e40f7aec22ee3951378a555e5c8775aaf66 Mon Sep 17 00:00:00 2001 From: prssanna Date: Mon, 1 Feb 2021 19:50:27 +0530 Subject: [PATCH 603/728] style: fix formatting --- .../accounts/doctype/account/account_tree.js | 2 +- erpnext/crm/doctype/contract/contract_list.js | 20 +++++++++---------- .../course_scheduling_tool.js | 4 ++-- .../healthcare_service_unit_tree.js | 8 ++++---- erpnext/hr/page/team_updates/team_updates.js | 2 +- erpnext/public/scss/shopping_cart.scss | 2 +- .../page/point_of_sale/point_of_sale.js | 2 +- erpnext/shopping_cart/search.py | 3 +-- erpnext/templates/includes/cart.js | 6 ++++-- 9 files changed, 25 insertions(+), 24 deletions(-) diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index 7dc0533506..7516134baf 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -123,7 +123,7 @@ frappe.treeview_settings["Account"] = { }, "add"); }, onrender: function(node) { - if(frappe.boot.user.can_read.indexOf("GL Entry") !== -1){ + if (frappe.boot.user.can_read.indexOf("GL Entry") !== -1) { // show Dr if positive since balance is calculated as debit - credit else show Cr let balance = node.data.balance_in_account_currency || node.data.balance; diff --git a/erpnext/crm/doctype/contract/contract_list.js b/erpnext/crm/doctype/contract/contract_list.js index a09d123ed2..26a2907c7c 100644 --- a/erpnext/crm/doctype/contract/contract_list.js +++ b/erpnext/crm/doctype/contract/contract_list.js @@ -1,12 +1,12 @@ frappe.listview_settings['Contract'] = { - add_fields: ["status"], - get_indicator: function (doc) { - if (doc.status == "Unsigned") { - return [__(doc.status), "red", "status,=," + doc.status]; - } else if (doc.status == "Active") { - return [__(doc.status), "green", "status,=," + doc.status]; - } else if (doc.status == "Inactive") { - return [__(doc.status), "gray", "status,=," + doc.status]; - } - }, + add_fields: ["status"], + get_indicator: function (doc) { + if (doc.status == "Unsigned") { + return [__(doc.status), "red", "status,=," + doc.status]; + } else if (doc.status == "Active") { + return [__(doc.status), "green", "status,=," + doc.status]; + } else if (doc.status == "Inactive") { + return [__(doc.status), "gray", "status,=," + doc.status]; + } + }, }; \ No newline at end of file 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 4e2ccaa30c..8699103cb2 100644 --- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js +++ b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js @@ -24,8 +24,8 @@ frappe.ui.form.on('Course Scheduling Tool', {
    - ${course_schedules.map( - c => ` + ${course_schedules.map(c => + `` ).join('')} diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js index bfb0ba13ce..b75f271827 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js @@ -12,19 +12,19 @@ frappe.treeview_settings["Healthcare Service Unit"] = { get_tree_nodes: 'erpnext.healthcare.utils.get_children', ignore_fields:["parent_healthcare_service_unit"], onrender: function(node) { - if (node.data.occupied_out_of_vacant!==undefined){ + if (node.data.occupied_out_of_vacant!==undefined) { $('' + " " + node.data.occupied_out_of_vacant + '').insertBefore(node.$ul); } if (node.data && node.data.inpatient_occupancy!==undefined) { - if (node.data.inpatient_occupancy == 1){ - if (node.data.occupancy_status == "Occupied"){ + if (node.data.inpatient_occupancy == 1) { + if (node.data.occupancy_status == "Occupied") { $('' + " " + node.data.occupancy_status + '').insertBefore(node.$ul); } - if (node.data.occupancy_status == "Vacant"){ + if (node.data.occupancy_status == "Vacant") { $('' + " " + node.data.occupancy_status + '').insertBefore(node.$ul); diff --git a/erpnext/hr/page/team_updates/team_updates.js b/erpnext/hr/page/team_updates/team_updates.js index 29780b8161..13d0074660 100644 --- a/erpnext/hr/page/team_updates/team_updates.js +++ b/erpnext/hr/page/team_updates/team_updates.js @@ -41,7 +41,7 @@ frappe.team_updates = { me.add_row(d); }); } else { - frappe.show_alert({message:__('No more updates'), indicator:'gray'}); + frappe.show_alert({message: __('No more updates'), indicator: 'gray'}); me.more.parent().addClass('hidden'); } } diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss index ee30f53186..159a8a47cd 100644 --- a/erpnext/public/scss/shopping_cart.scss +++ b/erpnext/public/scss/shopping_cart.scss @@ -58,7 +58,7 @@ body.product-page { .card-grid { display: grid; grid-gap: 15px; - grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));; + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); } } diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index 6d8ad7e1af..501c7df78b 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -10,7 +10,7 @@ frappe.pages['point-of-sale'].on_page_load = function(wrapper) { frappe.require('assets/js/point-of-sale.min.js', function() { wrapper.pos = new erpnext.PointOfSale.Controller(wrapper); window.cur_pos = wrapper.pos; - }) + }); }; frappe.pages['point-of-sale'].refresh = function(wrapper) { diff --git a/erpnext/shopping_cart/search.py b/erpnext/shopping_cart/search.py index 012d09fafc..63e9fe1b31 100644 --- a/erpnext/shopping_cart/search.py +++ b/erpnext/shopping_cart/search.py @@ -1,7 +1,6 @@ import frappe from frappe.search.full_text_search import FullTextSearch from whoosh.fields import TEXT, ID, KEYWORD, Schema -from frappe.website.render import render_page from frappe.utils import strip_html_tags from whoosh.qparser import MultifieldParser, FieldsPlugin, WildcardPlugin from whoosh.analysis import StemmingAnalyzer @@ -52,7 +51,7 @@ class ProductSearch(FullTextSearch): if item.web_long_description: content = strip_html_tags(item.web_long_description) - elif description: + elif item.description: content = strip_html_tags(item.description) return frappe._dict( diff --git a/erpnext/templates/includes/cart.js b/erpnext/templates/includes/cart.js index 0de02676af..c390cd171d 100644 --- a/erpnext/templates/includes/cart.js +++ b/erpnext/templates/includes/cart.js @@ -35,7 +35,7 @@ $.extend(shopping_cart, { }, get_update_address_dialog() { - return new frappe.ui.Dialog({ + let d = new frappe.ui.Dialog({ title: "Select Address", fields: [{ 'fieldtype': 'HTML', @@ -56,7 +56,7 @@ $.extend(shopping_cart, { }, callback: function(r) { d.hide(); - if(!r.exc) { + if (!r.exc) { $(".cart-tax-items").html(r.message.taxes); shopping_cart.parent.find( `.address-container[data-address-type="${address_type}"]` @@ -66,6 +66,8 @@ $.extend(shopping_cart, { }); } }); + + return d; }, get_address_template(type) { From 1ab79da7d1324e386d2789e9e47277b9d642d124 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 1 Feb 2021 19:51:25 +0530 Subject: [PATCH 604/728] style: Fix formatting issues --- .eslintrc | 3 +- .../page/point_of_sale/point_of_sale.js | 4 +- .../page/point_of_sale/pos_controller.js | 8 +- .../selling/page/point_of_sale/pos_payment.js | 110 +++++++++--------- 4 files changed, 60 insertions(+), 65 deletions(-) diff --git a/.eslintrc b/.eslintrc index 91ebb2b84f..3b6ab7498d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -150,6 +150,7 @@ "it": true, "context": true, "before": true, - "beforeEach": true + "beforeEach": true, + "onScan": true } } diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index 6d8ad7e1af..e3405e0ce8 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -10,7 +10,7 @@ frappe.pages['point-of-sale'].on_page_load = function(wrapper) { frappe.require('assets/js/point-of-sale.min.js', function() { wrapper.pos = new erpnext.PointOfSale.Controller(wrapper); window.cur_pos = wrapper.pos; - }) + }); }; frappe.pages['point-of-sale'].refresh = function(wrapper) { @@ -19,4 +19,4 @@ frappe.pages['point-of-sale'].refresh = function(wrapper) { wrapper.pos.wrapper.html(""); wrapper.pos.check_opening_entry(); } -} \ No newline at end of file +}; \ 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 dc1e3bf20a..bf70f0785f 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -191,8 +191,8 @@ erpnext.PointOfSale.Controller = class { this.frm.save(undefined, undefined, undefined, () => { frappe.show_alert({ - message:__("There was an error saving the document."), - indicator:'red' + message: __("There was an error saving the document."), + indicator: 'red' }); frappe.utils.play_sound("error"); }).then(() => { @@ -201,7 +201,7 @@ erpnext.PointOfSale.Controller = class { () => this.make_new_invoice(), () => frappe.dom.unfreeze(), ]); - }) + }); } close_pos() { @@ -682,5 +682,5 @@ erpnext.PointOfSale.Controller = class { }) .catch(e => console.log(e)); } -} +}; diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 365c27b12f..fb26f22625 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars */ erpnext.PointOfSale.Payment = class { constructor({ events, wrapper }) { this.wrapper = wrapper; @@ -11,7 +12,7 @@ erpnext.PointOfSale.Payment = class { this.initialize_numpad(); this.bind_events(); this.attach_shortcuts(); - + } prepare_dom() { @@ -31,7 +32,7 @@ erpnext.PointOfSale.Payment = class {
    Complete Order
    ` - ) + ); this.$component = this.wrapper.find('.payment-container'); this.$payment_modes = this.$component.find('.payment-modes'); this.$totals_section = this.$component.find('.totals-section'); @@ -44,7 +45,7 @@ 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(); @@ -54,8 +55,10 @@ erpnext.PointOfSale.Payment = class { `
    ` ); let df_events = { - onchange: function() { frm.set_value(this.df.fieldname, this.value); } - } + onchange: function() { + frm.set_value(this.df.fieldname, this.value); + } + }; if (df.fieldtype == "Button") { df_events = { click: function() { @@ -63,11 +66,11 @@ erpnext.PointOfSale.Payment = class { frm.script_manager.trigger(df.fieldname, frm.doc.doctype, frm.doc.docname); } } - } + }; } this[`${df.fieldname}_field`] = frappe.ui.form.make_control({ - df: { + df: { ...df, ...df_events }, @@ -75,7 +78,7 @@ erpnext.PointOfSale.Payment = class { render_input: true, }); this[`${df.fieldname}_field`].set_value(frm.doc[df.fieldname]); - }) + }); }); } @@ -95,13 +98,12 @@ erpnext.PointOfSale.Payment = class { [ 7, 8, 9 ], [ '.', 0, 'Delete' ] ], - }) + }); this.numpad_value = ''; } on_numpad_clicked($btn) { - const me = this; const button_value = $btn.attr('data-button-value'); highlight_numpad_btn($btn); @@ -155,10 +157,10 @@ erpnext.PointOfSale.Payment = class { 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 ? + !current_value && doc.grand_total > doc.paid_amount && me.selected_mode ? me.selected_mode.set_value(doc.grand_total - doc.paid_amount) : ''; } - }) + }); frappe.realtime.on("process_phone_payment", function(data) { frappe.dom.unfreeze(); @@ -168,7 +170,7 @@ erpnext.PointOfSale.Payment = class { if (data["ResultCode"] == 0) { title = __("Payment Received"); - $('.btn.btn-xs.btn-default[data-fieldname=request_for_payment]').html(`Payment Received`) + $('.btn.btn-xs.btn-default[data-fieldname=request_for_payment]').html(`Payment Received`); me.events.submit_invoice(); } @@ -178,10 +180,10 @@ erpnext.PointOfSale.Payment = class { }); }); - this.$payment_modes.on('click', '.shortcut', function(e) { + this.$payment_modes.on('click', '.shortcut', () => { const value = $(this).attr('data-value'); me.selected_mode.set_value(value); - }) + }); this.$component.on('click', '.submit-order-btn', () => { const doc = this.events.get_frm().doc; @@ -189,14 +191,14 @@ erpnext.PointOfSale.Payment = class { const items = doc.items; if (paid_amount == 0 || !items.length) { - const message = items.length ? __("You cannot submit the order without payment.") : __("You cannot submit empty order.") + const message = items.length ? __("You cannot submit the order without payment.") : __("You cannot submit empty order."); frappe.show_alert({ message, indicator: "orange" }); frappe.utils.play_sound("error"); return; } this.events.submit_invoice(); - }) + }); frappe.ui.form.on('POS Invoice', 'paid_amount', (frm) => { this.update_totals_section(frm.doc); @@ -205,7 +207,7 @@ erpnext.PointOfSale.Payment = class { 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').css('display', 'grid'); - }) + }); frappe.ui.form.on('POS Invoice', 'loyalty_amount', (frm) => { const formatted_currency = format_currency(frm.doc.loyalty_amount, frm.doc.currency); @@ -239,14 +241,14 @@ erpnext.PointOfSale.Payment = class { const payment_is_visible = this.$component.is(":visible"); let active_mode = this.$payment_modes.find(".border-primary"); active_mode = active_mode.length ? active_mode.attr("data-mode") : undefined; - + if (!active_mode) return; - + const mode_of_payments = Array.from(this.$payment_modes.find(".mode-of-payment")).map(m => $(m).attr("data-mode")); const mode_index = mode_of_payments.indexOf(active_mode); const next_mode_index = (mode_index + 1) % mode_of_payments.length; const next_mode_to_be_clicked = this.$payment_modes.find(`.mode-of-payment[data-mode="${mode_of_payments[next_mode_index]}"]`); - + if (payment_is_visible && mode_index != next_mode_index) { next_mode_to_be_clicked.click(); } @@ -258,14 +260,8 @@ erpnext.PointOfSale.Payment = class { }); } - toggle_numpad(show) { - // 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'); - // } + toggle_numpad() { + // pass } render_payment_section() { @@ -309,9 +305,8 @@ erpnext.PointOfSale.Payment = class { const payments = doc.payments; const currency = doc.currency; - this.$payment_modes.html( - `${ - payments.map((p, i) => { + this.$payment_modes.html(`${ + payments.map((p, i) => { const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase(); const payment_type = p.type; const margin = i % 2 === 0 ? 'pr-2' : 'pl-2'; @@ -319,16 +314,15 @@ erpnext.PointOfSale.Payment = class { return ( `
    -
    - ${p.mode_of_payment} -
    ${amount}
    -
    -
    -
    ` - ) - }).join('') - }` - ) +
    + ${p.mode_of_payment} +
    ${amount}
    +
    +
    + ` + ); + }).join('') + }`); payments.forEach(p => { const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase(); @@ -359,10 +353,10 @@ erpnext.PointOfSale.Payment = class { this.$payment_modes.find(`.${mode}.mode-of-payment-control`).parent().click(); }, 500); } - }) + }); this.render_loyalty_points_payment_mode(); - + this.attach_cash_shortcuts(doc); } @@ -376,12 +370,12 @@ erpnext.PointOfSale.Payment = class { this.$payment_modes.find('[data-payment-type="Cash"]').find('.mode-of-payment-control').after( `
    ${ - shortcuts.map(s => { - return `
    ${format_currency(s, currency, 0)}
    ` - }).join('') - } + shortcuts.map(s => { + return `
    ${format_currency(s, currency, 0)}
    `; + }).join('') +}
    ` - ) + ); } get_cash_shortcuts(grand_total) { @@ -393,13 +387,13 @@ erpnext.PointOfSale.Payment = class { const get_nearest = (amount, x) => { let nearest_x = Math.ceil((amount / x)) * x; return nearest_x === amount ? nearest_x + x : nearest_x; - } + }; return steps.reduce((finalArr, x) => { let nearest_x = get_nearest(grand_total, x); nearest_x = finalArr.indexOf(nearest_x) != -1 ? nearest_x + x : nearest_x; return [...finalArr, nearest_x]; - }, []); + }, []); } render_loyalty_points_payment_mode() { @@ -408,7 +402,7 @@ erpnext.PointOfSale.Payment = class { const { loyalty_program, loyalty_points, conversion_factor } = this.events.get_customer_details(); this.$payment_modes.find(`.mode-of-payment[data-mode="loyalty-amount"]`).parent().remove(); - + if (!loyalty_program) return; let description, read_only, max_redeemable_amount; @@ -416,7 +410,7 @@ erpnext.PointOfSale.Payment = class { description = __("You don't have enough points to redeem."); read_only = true; } else { - max_redeemable_amount = flt(flt(loyalty_points) * flt(conversion_factor), precision("loyalty_amount", doc)) + max_redeemable_amount = flt(flt(loyalty_points) * flt(conversion_factor), precision("loyalty_amount", doc)); description = __("You can redeem upto {0}.", [format_currency(max_redeemable_amount)]); read_only = false; } @@ -432,7 +426,7 @@ erpnext.PointOfSale.Payment = class {
    ` - ) + ); this['loyalty-amount_control'] = frappe.ui.form.make_control({ df: { @@ -474,7 +468,7 @@ erpnext.PointOfSale.Payment = class { `
    + Add Payment Method
    ` - ) + ); } update_totals_section(doc) { @@ -482,7 +476,7 @@ erpnext.PointOfSale.Payment = class { const paid_amount = doc.paid_amount; const remaining = doc.grand_total - doc.paid_amount; const change = doc.change_amount || remaining <= 0 ? -1 * remaining : undefined; - const currency = doc.currency + const currency = doc.currency; const label = change ? __('Change') : __('To Be Paid'); this.$totals.html( @@ -500,10 +494,10 @@ erpnext.PointOfSale.Payment = class {
    ${label}
    ${format_currency(change || remaining, currency)}
    ` - ) + ); } toggle_component(show) { show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none'); } - } \ No newline at end of file +}; \ No newline at end of file From 11eae6d51a700b86e7a4257762ca114baa07616a Mon Sep 17 00:00:00 2001 From: prssanna Date: Mon, 1 Feb 2021 20:01:37 +0530 Subject: [PATCH 605/728] fix: add default product-image css class --- erpnext/templates/includes/macros.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html index 174b14163e..c44bfb5384 100644 --- a/erpnext/templates/includes/macros.html +++ b/erpnext/templates/includes/macros.html @@ -7,7 +7,7 @@ {% endmacro %} -{% macro product_image(website_image, css_class="", alt="") %} +{% macro product_image(website_image, css_class="product-image", alt="") %}
    {{ alt }}
    From fb016edb7a776a17bceccf98fc55c72ffd195874 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 1 Feb 2021 20:05:29 +0530 Subject: [PATCH 606/728] chore: Use sql to set naming series in older projects (#24512) * chore: Use sql to set naming series in older projects * fix: Remove unncessary db.commit() --- erpnext/patches.txt | 2 +- .../add_naming_series_to_old_projects.py | 23 ++++--------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 348f98fa74..a2b3dda4ef 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -749,4 +749,4 @@ erpnext.patches.v13_0.update_project_template_tasks erpnext.patches.v13_0.set_company_in_leave_ledger_entry erpnext.patches.v13_0.convert_qi_parameter_to_link_field erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes -erpnext.patches.v13_0.add_naming_series_to_old_projects +erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021 diff --git a/erpnext/patches/v13_0/add_naming_series_to_old_projects.py b/erpnext/patches/v13_0/add_naming_series_to_old_projects.py index 79b67533ed..5ed9040f1e 100644 --- a/erpnext/patches/v13_0/add_naming_series_to_old_projects.py +++ b/erpnext/patches/v13_0/add_naming_series_to_old_projects.py @@ -4,23 +4,10 @@ from frappe.custom.doctype.property_setter.property_setter import make_property_ def execute(): frappe.reload_doc("projects", "doctype", "project") - projects = frappe.db.get_all("Project", - fields=["name", "naming_series", "modified"], - filters={ - "naming_series": ["is", "not set"] - }, - order_by="timestamp(modified) asc") - # disable set only once as the old docs must be saved - # (to bypass 'Cant change naming series' validation on save) - make_property_setter("Project", "naming_series", "set_only_once", 0, "Check") + frappe.db.sql("""UPDATE `tabProject` + SET + naming_series = 'PROJ-.####' + WHERE + naming_series is NULL""") - for entry in projects: - # need to save the doc so that users can edit old projects - doc = frappe.get_doc("Project", entry.name) - if not doc.naming_series: - doc.naming_series = "PROJ-.####" - doc.save() - - delete_property_setter("Project", "set_only_once", "naming_series") - frappe.db.commit() From f57efd1150af89c626515f7c78803897fccf56d7 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 1 Feb 2021 20:09:20 +0530 Subject: [PATCH 607/728] style: Fix invalid translation syntax usages --- erpnext/assets/doctype/asset/asset.py | 7 ++++--- erpnext/hr/doctype/employee_transfer/employee_transfer.py | 5 +++-- erpnext/setup/doctype/company/company.py | 5 +++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 1793dad494..e8e8ec6cc0 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -133,9 +133,10 @@ class Asset(AccountsController): if self.is_existing_asset: return if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount: - frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\ - Please do not book expense of multiple assets against one single Asset.") - .format(frappe.bold("equal"), "
    "), title=_("Invalid Gross Purchase Amount")) + error_message = _("Gross Purchase Amount should be equal to purchase amount of one single Asset.") + error_message += "
    " + error_message += _("Please do not book expense of multiple assets against one single Asset.") + frappe.throw(error_message, title=_("Invalid Gross Purchase Amount")) def make_asset_movement(self): reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice' diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py index 37d616f14d..3539970a32 100644 --- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py +++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py @@ -50,8 +50,9 @@ 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}\ - to cancel this document").format(self.new_employee_id)) + frappe.throw(_("Please delete the Employee {0} to cancel this document").format( + "{0}".format(self.new_employee_id) + )) #mark the employee as active employee.status = "Active" employee.relieving_date = '' diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index dc45cc1f7e..819ba78e66 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -89,8 +89,9 @@ class Company(NestedSet): frappe.throw(_("Account {0} does not belong to company: {1}").format(self.get(account[1]), self.name)) if get_account_currency(self.get(account[1])) != self.default_currency: - frappe.throw(_("""{0} currency must be same as company's default currency. - Please select another account""").format(frappe.bold(account[0]))) + error_message = _("{0} currency must be same as company's default currency. Please select another account.") \ + .format(frappe.bold(account[0])) + frappe.throw(error_message) def validate_currency(self): if self.is_new(): From 8586c08639439da78dbeecaa5f1b588bf5fdf32e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 1 Feb 2021 20:09:42 +0530 Subject: [PATCH 608/728] chore: Update translation syntax checker --- .github/helper/translation.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/helper/translation.py b/.github/helper/translation.py index 340f4f8772..9146b3b32b 100644 --- a/.github/helper/translation.py +++ b/.github/helper/translation.py @@ -2,7 +2,7 @@ import re import sys errors_encounter = 0 -pattern = re.compile(r"_\(([\"']{,3})(?P((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P((?!\5).)*)\5)*(\s*,\s*(.)*?\s*(,\s*([\"'])(?P((?!\11).)*)\11)*)*\)") +pattern = re.compile(r"_\(([\"']{,3})(?P((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P((?!\11).)*)\11)*)*\)") words_pattern = re.compile(r"_{1,2}\([\"'`]{1,3}.*?[a-zA-Z]") start_pattern = re.compile(r"_{1,2}\([f\"'`]{1,3}") f_string_pattern = re.compile(r"_\(f[\"']") @@ -28,7 +28,7 @@ for _file in files_to_scan: has_f_string = f_string_pattern.search(line) if has_f_string: errors_encounter += 1 - print(f'\nF-strings are not supported for translations at line number {line_number + 1}\n{line.strip()[:100]}') + print(f'\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}') continue else: continue @@ -36,7 +36,7 @@ for _file in files_to_scan: match = pattern.search(line) error_found = False - if not match and line.endswith(',\n'): + if not match and line.endswith((',\n', '[\n')): # concat remaining text to validate multiline pattern line = "".join(file_lines[line_number - 1:]) line = line[start_matches.start() + 1:] @@ -44,11 +44,11 @@ for _file in files_to_scan: if not match: error_found = True - print(f'\nTranslation syntax error at line number {line_number + 1}\n{line.strip()[:100]}') + print(f'\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}') if not error_found and not words_pattern.search(line): error_found = True - print(f'\nTranslation is useless because it has no words at line number {line_number + 1}\n{line.strip()[:100]}') + print(f'\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}') if error_found: errors_encounter += 1 From 526ea46ae2c4aa284d1ef5b5829016fd54f9e70c Mon Sep 17 00:00:00 2001 From: prssanna Date: Mon, 1 Feb 2021 20:12:47 +0530 Subject: [PATCH 609/728] fix: formatting issues --- .../course_scheduling_tool.js | 7 +++-- .../page/point_of_sale/pos_controller.js | 2 +- .../page/point_of_sale/pos_item_cart.js | 31 ++++++++++--------- .../page/point_of_sale/pos_item_details.js | 4 +-- .../page/point_of_sale/pos_number_pad.js | 6 ++-- .../generators/item/item_configure.js | 2 ++ 6 files changed, 28 insertions(+), 24 deletions(-) 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 8699103cb2..907d79b3b8 100644 --- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js +++ b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js @@ -19,8 +19,8 @@ frappe.ui.form.on('Course Scheduling Tool', { } const { course_schedules } = r.message; if (course_schedules) { - const html = ` -
    ${__('Following course schedules were created')}
    ${__("Course")}${__("Date")}
    ${c.name}
    ${c.name} ${c.schedule_date}
    + /* eslint-disable indent */ + const html = `
    @@ -29,7 +29,8 @@ frappe.ui.form.on('Course Scheduling Tool', { ` ).join('')} -
    ${__('Following course schedules were created')}
    ${__("Course")}${__("Date")}
    ${c.schedule_date}
    ` + `; + /* eslint-disable indent */ frappe.msgprint(html); } diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index bf70f0785f..338a3ccf24 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -182,7 +182,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"); 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 00c409dcd3..abd1f43a51 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -155,11 +155,11 @@ erpnext.PointOfSale.ItemCart = class { bind_events() { const me = this; - this.$customer_section.on('click', '.reset-customer-btn', function (e) { + this.$customer_section.on('click', '.reset-customer-btn', function () { me.reset_customer_selector(); }); - this.$customer_section.on('click', '.close-details-btn', function (e) { + this.$customer_section.on('click', '.close-details-btn', function () { me.toggle_customer_info(false); }); @@ -365,7 +365,7 @@ erpnext.PointOfSale.ItemCart = class { } show_discount_control() { - this.$add_discount_elem.css({ 'padding': '0px', 'border': 'none' }) + this.$add_discount_elem.css({ 'padding': '0px', 'border': 'none' }); this.$add_discount_elem.html( `
    ` ); @@ -447,13 +447,13 @@ 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}
    `; } } @@ -462,9 +462,9 @@ erpnext.PointOfSale.ItemCart = class { get_customer_image() { const { customer, image } = this.customer_info || {}; if (image) { - return `
    ${image}
    ` + return `
    ${image}
    `; } else { - return `
    ${frappe.get_abbr(customer)}
    ` + return `
    ${frappe.get_abbr(customer)}
    `; } } @@ -501,7 +501,7 @@ erpnext.PointOfSale.ItemCart = class { this.$numpad_section.find('.numpad-grand-total').html( `
    Grand Total: ${format_currency(value, currency)}
    ` - ) + ); } render_taxes(value, taxes) { @@ -509,6 +509,7 @@ erpnext.PointOfSale.ItemCart = class { const currency = this.events.get_frm().doc.currency; this.$totals_section.find('.taxes-container').css('display', 'flex').html( `${ + // eslint-disable-next-line no-unused-vars taxes.map((t, i) => { const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; return `
    @@ -519,7 +520,7 @@ erpnext.PointOfSale.ItemCart = class {
    ` }).join('') }` - ) + ); } else { this.$totals_section.find('.taxes-container').css('display', 'none').html(''); } @@ -633,7 +634,7 @@ erpnext.PointOfSale.ItemCart = class { } } item_data.description = frappe.ellipsis(item_data.description, 45); - return `
    ${item_data.description}
    ` + return `
    ${item_data.description}
    `; } return ``; } @@ -641,9 +642,9 @@ erpnext.PointOfSale.ItemCart = class { function get_item_image_html() { const { image, item_name } = item_data; if (image) { - return `
    ${image}
    ` + return `
    ${image}
    `; } else { - return `
    ${frappe.get_abbr(item_name)}
    ` + return `
    ${frappe.get_abbr(item_name)}
    `; } } } @@ -980,7 +981,7 @@ erpnext.PointOfSale.ItemCart = class { this.fetch_customer_details(frm.doc.customer).then(() => { this.events.customer_details_updated(this.customer_info); this.update_customer_section(); - }) + }); this.$cart_items_wrapper.html(''); if (frm.doc.items.length) { 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 f3aa1fc0c6..cb0a0103e0 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_details.js +++ b/erpnext/selling/page/point_of_sale/pos_item_details.js @@ -183,11 +183,11 @@ erpnext.PointOfSale.ItemDetails = class { if (!item.has_batch_no) { this.$form_container.append( `
    ` - ) + ); } this.$form_container.append( `
    Auto Fetch Serial Numbers
    ` - ) + ); this.$form_container.find('.serial_no-control').find('textarea').css('height', '6rem'); } } 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 edde7d84df..962bcaf096 100644 --- a/erpnext/selling/page/point_of_sale/pos_number_pad.js +++ b/erpnext/selling/page/point_of_sale/pos_number_pad.js @@ -22,11 +22,11 @@ erpnext.PointOfSale.NumberPad = class { return keys.reduce((a, row, i) => { return a + row.reduce((a2, number, j) => { const class_to_append = css_classes && css_classes[i] ? css_classes[i][j] : ''; - const fieldname = fieldnames && fieldnames[number] ? + const fieldname = fieldnames && fieldnames[number] ? fieldnames[number] : typeof number === 'string' ? frappe.scrub(number) : number; - return a2 + `
    ${number}
    ` - }, '') + return a2 + `
    ${number}
    `; + }, ''); }, ''); } diff --git a/erpnext/templates/generators/item/item_configure.js b/erpnext/templates/generators/item/item_configure.js index 276837ea61..8eadb84289 100644 --- a/erpnext/templates/generators/item/item_configure.js +++ b/erpnext/templates/generators/item/item_configure.js @@ -209,6 +209,7 @@ class ItemConfigure { __('{0} item found.', [filtered_items_count]) : __('{0} items found.', [filtered_items_count]); + /* eslint-disable indent */ const item_found_status = exact_match.length === 1 ? ``; }).join('') }` ); @@ -844,7 +844,7 @@ erpnext.PointOfSale.ItemCart = class {
    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_fields(); this.fetch_customer_transactions(); From 73d60aa498c61abad86035d5d8864182a8e70dd2 Mon Sep 17 00:00:00 2001 From: prssanna Date: Mon, 1 Feb 2021 20:38:17 +0530 Subject: [PATCH 615/728] style: fix formatting --- .../page/point_of_sale/pos_item_cart.js | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) 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 abd1f43a51..52d345cbfc 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -507,20 +507,14 @@ erpnext.PointOfSale.ItemCart = class { render_taxes(value, taxes) { if (taxes.length) { const currency = this.events.get_frm().doc.currency; - this.$totals_section.find('.taxes-container').css('display', 'flex').html( - `${ - // eslint-disable-next-line no-unused-vars - taxes.map((t, i) => { - const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; - return `
    -
    - ${description} -
    -
    ${format_currency(value, currency)}
    -
    ` - }).join('') - }` - ); + const taxes_html = taxes.map(t => { + const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; + return `
    +
    ${description}
    +
    ${format_currency(value, currency)}
    +
    ` + }).join(''); + this.$totals_section.find('.taxes-container').css('display', 'flex').html(taxes_html); } else { this.$totals_section.find('.taxes-container').css('display', 'none').html(''); } @@ -844,7 +838,7 @@ erpnext.PointOfSale.ItemCart = class {
    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_fields(); this.fetch_customer_transactions(); From 0ea2587679b5922d539e61880e18741e01526d13 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 1 Feb 2021 20:38:53 +0530 Subject: [PATCH 616/728] fix: Numeric/Non-numeric QI UX (#24516) * chore: Show 1 field each of both types of Insoections in grid view * fix: Make QI check Numeric by default and make checkbox "Numeric" - Reducing cognitive load --- .../item_quality_inspection_parameter.json | 16 ++++----- .../quality_inspection/quality_inspection.py | 4 +-- .../test_quality_inspection.py | 4 +-- .../quality_inspection_reading.json | 34 +++++++++---------- .../quality_inspection_template.py | 2 +- 5 files changed, 30 insertions(+), 30 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 3e81619cfd..471e6853b5 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 @@ -8,7 +8,7 @@ "field_order": [ "specification", "value", - "non_numeric", + "numeric", "column_break_3", "min_value", "max_value", @@ -29,7 +29,7 @@ "width": "100px" }, { - "depends_on": "eval:(!doc.formula_based_criteria && doc.non_numeric)", + "depends_on": "eval:(!doc.formula_based_criteria && !doc.numeric)", "fieldname": "value", "fieldtype": "Data", "in_list_view": 1, @@ -55,32 +55,32 @@ "label": "Formula Based Criteria" }, { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.non_numeric)", + "depends_on": "eval:(!doc.formula_based_criteria && doc.numeric)", "fieldname": "min_value", "fieldtype": "Float", "in_list_view": 1, "label": "Minimum Value" }, { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.non_numeric)", + "depends_on": "eval:(!doc.formula_based_criteria && doc.numeric)", "fieldname": "max_value", "fieldtype": "Float", "in_list_view": 1, "label": "Maximum Value" }, { - "default": "0", - "fieldname": "non_numeric", + "default": "1", + "fieldname": "numeric", "fieldtype": "Check", "in_list_view": 1, - "label": "Non-Numeric", + "label": "Numeric", "width": "80px" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-01-07 21:32:49.866439", + "modified": "2021-02-01 19:18:46.924399", "modified_by": "Administrator", "module": "Stock", "name": "Item Quality Inspection Parameter", diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index b3acbc5ba0..58b1eca2d3 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -97,7 +97,7 @@ class QualityInspection(Document): self.set_status_based_on_acceptance_values(reading) def set_status_based_on_acceptance_values(self, reading): - if cint(reading.non_numeric): + if not cint(reading.numeric): result = reading.get("reading_value") == reading.get("value") else: # numeric readings @@ -136,7 +136,7 @@ class QualityInspection(Document): def get_formula_evaluation_data(self, reading): data = {} - if cint(reading.non_numeric): + if not cint(reading.numeric): data = {"reading_value": reading.get("reading_value")} else: # numeric readings diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py index 8c5a04b3f0..a7dfc9ee28 100644 --- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py @@ -55,7 +55,7 @@ class TestQualityInspection(unittest.TestCase): }, { "specification": "Particle Inspection Needed", # non-numeric reading - "non_numeric": 1, + "numeric": 0, "value": "Yes", "reading_value": "Yes" }] @@ -96,7 +96,7 @@ class TestQualityInspection(unittest.TestCase): { "specification": "Calcium Content", # non-numeric reading "formula_based_criteria": 1, - "non_numeric": 1, + "numeric": 0, "acceptance_formula": "reading_value in ('Grade A', 'Grade B', 'Grade C')", "reading_value": "Grade B" }] 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 30ff1fea3a..35d58eff58 100644 --- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json +++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json @@ -9,7 +9,7 @@ "specification", "status", "value", - "non_numeric", + "numeric", "manual_inspection", "column_break_4", "min_value", @@ -46,7 +46,7 @@ }, { "columns": 2, - "depends_on": "eval:(!doc.formula_based_criteria && doc.non_numeric)", + "depends_on": "eval:(!doc.formula_based_criteria && !doc.numeric)", "fieldname": "value", "fieldtype": "Data", "label": "Acceptance Criteria Value", @@ -54,7 +54,7 @@ "oldfieldtype": "Data" }, { - "columns": 1, + "columns": 2, "fieldname": "reading_1", "fieldtype": "Data", "in_list_view": 1, @@ -66,7 +66,6 @@ "columns": 1, "fieldname": "reading_2", "fieldtype": "Data", - "in_list_view": 1, "label": "Reading 2", "oldfieldname": "reading_2", "oldfieldtype": "Data" @@ -140,7 +139,7 @@ "options": "\nAccepted\nRejected" }, { - "depends_on": "non_numeric", + "depends_on": "eval:!doc.numeric", "fieldname": "section_break_3", "fieldtype": "Section Break", "label": "Value Based Inspection" @@ -171,51 +170,52 @@ "label": "Formula Based Criteria" }, { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.non_numeric)", + "depends_on": "eval:(!doc.formula_based_criteria && doc.numeric)", "description": "Applied on each reading.", "fieldname": "min_value", "fieldtype": "Float", "label": "Minimum Value" }, { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.non_numeric)", + "depends_on": "eval:(!doc.formula_based_criteria && doc.numeric)", "description": "Applied on each reading.", "fieldname": "max_value", "fieldtype": "Float", "label": "Maximum Value" }, { - "depends_on": "non_numeric", + "columns": 2, + "depends_on": "eval:!doc.numeric", "fieldname": "reading_value", "fieldtype": "Data", "in_list_view": 1, "label": "Reading Value" }, { - "depends_on": "eval:!doc.non_numeric", + "depends_on": "numeric", "fieldname": "section_break_14", "fieldtype": "Section Break", "label": "Numeric Inspection" }, - { - "default": "0", - "fieldname": "non_numeric", - "fieldtype": "Check", - "in_list_view": 1, - "label": "Non-Numeric" - }, { "default": "0", "description": "Set the status manually.", "fieldname": "manual_inspection", "fieldtype": "Check", "label": "Manual Inspection" + }, + { + "default": "1", + "fieldname": "numeric", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Numeric" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-01-07 22:16:53.978410", + "modified": "2021-02-01 19:46:22.138018", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection Reading", 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 c5a7974a73..01d2031b3a 100644 --- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py +++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py @@ -14,6 +14,6 @@ def get_template_details(template): return frappe.get_all('Item Quality Inspection Parameter', fields=["specification", "value", "acceptance_formula", - "non_numeric", "formula_based_criteria", "min_value", "max_value"], + "numeric", "formula_based_criteria", "min_value", "max_value"], filters={'parenttype': 'Quality Inspection Template', 'parent': template}, order_by="idx") \ No newline at end of file From 6f1664223d965407c7b6717d60628bc62f10473c Mon Sep 17 00:00:00 2001 From: prssanna Date: Mon, 1 Feb 2021 20:43:30 +0530 Subject: [PATCH 617/728] style: missing semicolon --- erpnext/selling/page/point_of_sale/pos_item_cart.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 52d345cbfc..f753b6d5c5 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -512,7 +512,7 @@ erpnext.PointOfSale.ItemCart = class { return `
    ${description}
    ${format_currency(value, currency)}
    -
    ` +
    `; }).join(''); this.$totals_section.find('.taxes-container').css('display', 'flex').html(taxes_html); } else { From 852e67c5eea95e9344404c7a38a08c915094cbfa Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 1 Feb 2021 22:40:40 +0530 Subject: [PATCH 618/728] fix(patch): Use db_set to ignore validation --- erpnext/patches/v13_0/set_app_name.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/patches/v13_0/set_app_name.py b/erpnext/patches/v13_0/set_app_name.py index 0c78b65770..3f886f1d15 100644 --- a/erpnext/patches/v13_0/set_app_name.py +++ b/erpnext/patches/v13_0/set_app_name.py @@ -2,7 +2,6 @@ import frappe from frappe import _ def execute(): - frappe.reload_doctype("System Settings") - settings = frappe.get_doc("System Settings") - settings.app_name = _("ERPNext") - settings.save() \ No newline at end of file + frappe.reload_doctype("System Settings") + settings = frappe.get_doc("System Settings") + settings.db_set("app_name", "ERPNext", commit=True) From d4d639c7edc1dc27625d38af8883b72145e1594b Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 1 Feb 2021 22:58:22 +0530 Subject: [PATCH 619/728] fix: test cases for pricing rule --- .../doctype/pricing_rule/test_pricing_rule.py | 3 +++ erpnext/accounts/doctype/pricing_rule/utils.py | 11 +++++------ erpnext/controllers/taxes_and_totals.py | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index af8d21d9ce..f28cee7c5a 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -56,6 +56,7 @@ class TestPricingRule(unittest.TestCase): self.assertEqual(details.get("discount_percentage"), 10) prule = frappe.get_doc(test_record.copy()) + prule.priority = 1 prule.applicable_for = "Customer" prule.title = "_Test Pricing Rule for Customer" self.assertRaises(MandatoryError, prule.insert) @@ -261,6 +262,7 @@ class TestPricingRule(unittest.TestCase): "rate_or_discount": "Discount Percentage", "rate": 0, "discount_percentage": 17.5, + "priority": 1, "company": "_Test Company" }).insert() @@ -557,6 +559,7 @@ def make_pricing_rule(**args): "rate": args.rate or 0.0, "margin_rate_or_amount": args.margin_rate_or_amount or 0.0, "condition": args.condition or '', + "priority": 1, "apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0 }) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index bd9d0b3815..d163335996 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -59,18 +59,17 @@ def sorted_by_priority(pricing_rules, args, doc=None): pricing_rules_list = [] pricing_rule_dict = {} - priority = [] for pricing_rule in pricing_rules: pricing_rule = filter_pricing_rules(args, pricing_rule, doc) if pricing_rule: if not pricing_rule.get('priority'): - pricing_rules_list.append(pricing_rule) - else: - priority.append(cint(pricing_rule.get('priority'))) + pricing_rule['priority'] = 1 + + if pricing_rule.get('apply_multiple_pricing_rules'): pricing_rule_dict.setdefault(cint(pricing_rule.get("priority")), []).append(pricing_rule) - if priority: - pricing_rules_list.extend(pricing_rule_dict.get(min(priority))) + for key in sorted(pricing_rule_dict): + pricing_rules_list.extend(pricing_rule_dict.get(key)) return pricing_rules_list or pricing_rules diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 9f087e86a4..6c7eb92221 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -624,8 +624,8 @@ class calculate_taxes_and_totals(object): for d in get_applied_pricing_rules(item.pricing_rules): pricing_rule = frappe.get_cached_doc('Pricing Rule', d) - if (pricing_rule.margin_rate_or_amount and pricing_rule.currency == self.doc.currency and - pricing_rule.margin_type in ['Amount', 'Percentage']): + if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and + pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'): item.margin_type = pricing_rule.margin_type item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount has_margin = True From 91a02868cb70faa0887445886cfdfa4485e0f127 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 2 Feb 2021 11:04:25 +0530 Subject: [PATCH 620/728] fix: emp disappear (#24525) * fix: emp disappear * fix: renamed set_totals_call to set_totals Co-authored-by: Nabin Hait --- .../payroll/doctype/payroll_entry/payroll_entry.js | 1 - erpnext/payroll/doctype/salary_slip/salary_slip.js | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js index 45f9aa170f..395e56fa92 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js @@ -348,7 +348,6 @@ let render_employee_attendance = function (frm, data) { frappe.ui.form.on('Payroll Employee Detail', { employee: function(frm) { - frm.events.clear_employee_table(frm); if (!frm.doc.payroll_frequency) { frappe.throw(__("Please set a Payroll Frequency")); } diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js index b50c774fbe..7460c75227 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.js +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js @@ -116,7 +116,7 @@ frappe.ui.form.on("Salary Slip", { }, exchange_rate: function(frm) { - calculate_totals(frm); + set_totals(frm); }, hide_loan_section: function(frm) { @@ -205,14 +205,14 @@ frappe.ui.form.on("Salary Slip", { frappe.ui.form.on('Salary Slip Timesheet', { time_sheet: function(frm) { - calculate_totals(frm); + set_totals(frm); }, timesheets_remove: function(frm) { - calculate_totals(frm); + set_totals(frm); } }); -var calculate_totals = function(frm) { +var set_totals = function(frm) { if (frm.doc.docstatus === 0) { if (frm.doc.earnings || frm.doc.deductions) { frappe.call({ @@ -228,15 +228,15 @@ var calculate_totals = function(frm) { frappe.ui.form.on('Salary Detail', { amount: function(frm) { - calculate_totals(frm); + set_totals(frm); }, earnings_remove: function(frm) { - calculate_totals(frm); + set_totals(frm); }, deductions_remove: function(frm) { - calculate_totals(frm); + set_totals(frm); }, salary_component: function(frm, cdt, cdn) { From 7d64a291b8f99ed0d1cc5da175b53b0df578ccf1 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Tue, 2 Feb 2021 12:43:13 +0530 Subject: [PATCH 621/728] fix: contact permmission issue (#24503) --- erpnext/crm/doctype/lead/lead.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 1439adb015..938cbfdc85 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -176,7 +176,7 @@ class Lead(SellingController): "phone": self.mobile_no }) - contact.insert() + contact.insert(ignore_permissions=True) return contact From 71775790518bad69cb31ae0a4edf4ec482692068 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 2 Feb 2021 22:03:07 +0530 Subject: [PATCH 622/728] fix: Dependant sle logic fixes (#24489) * fix: Dependant sle logic fixes * fix: negative qty validation * fix: Travis fixes --- .../doctype/work_order/test_work_order.py | 4 +- .../united_states/test_united_states.py | 1 - .../setup/doctype/item_group/item_group.py | 2 - erpnext/shopping_cart/filters.py | 2 - erpnext/stock/doctype/bin/bin.py | 6 +-- .../repost_item_valuation.js | 2 +- erpnext/stock/stock_ledger.py | 48 +++++++------------ .../report/issue_analytics/issue_analytics.py | 3 +- .../issue_analytics/test_issue_analytics.py | 9 ++-- erpnext/www/all-products/index.py | 3 -- 10 files changed, 29 insertions(+), 51 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index e562d6c4bd..06a8e1987d 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -544,7 +544,7 @@ class TestWorkOrder(unittest.TestCase): 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)) - + ste_cancel_list.reverse() for ste_doc in ste_cancel_list: ste_doc.cancel() @@ -586,7 +586,7 @@ class TestWorkOrder(unittest.TestCase): for ste_row in ste2.items: if itemwise_qty.get(ste_row.item_code) and ste_row.s_warehouse: self.assertEquals(ste_row.qty, itemwise_qty.get(ste_row.item_code) / 2) - + ste_cancel_list.reverse() for ste_doc in ste_cancel_list: ste_doc.cancel() diff --git a/erpnext/regional/united_states/test_united_states.py b/erpnext/regional/united_states/test_united_states.py index ad95010a9a..513570ed6d 100644 --- a/erpnext/regional/united_states/test_united_states.py +++ b/erpnext/regional/united_states/test_united_states.py @@ -26,7 +26,6 @@ class TestUnitedStates(unittest.TestCase): make_payment_entry_to_irs_1099_supplier() filters = frappe._dict({"fiscal_year": "_Test Fiscal Year 2016", "company": "_Test Company 1"}) columns, data = execute_1099_report(filters) - print(columns, data) expected_row = {'supplier': '_US 1099 Test Supplier', 'supplier_group': 'Services', 'payments': 100.0, diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index e4cbf41f83..bff806d547 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -98,8 +98,6 @@ class ItemGroup(NestedSet, WebsiteGenerator): context.field_filters = filter_engine.get_field_filters() context.attribute_filters = filter_engine.get_attribute_fitlers() - print(context.field_filters, context.attribute_filters) - context.update({ "parents": get_parent_item_groups(self.parent_item_group), "title": self.name diff --git a/erpnext/shopping_cart/filters.py b/erpnext/shopping_cart/filters.py index e60364d92b..6c63d8759b 100644 --- a/erpnext/shopping_cart/filters.py +++ b/erpnext/shopping_cart/filters.py @@ -16,7 +16,6 @@ class ProductFiltersBuilder: def get_field_filters(self): filter_fields = [row.fieldname for row in self.doc.filter_fields] - print('FILTERS', self.doc.filter_fields) meta = frappe.get_meta('Item') fields = [df for df in meta.fields if df.fieldname in filter_fields] @@ -53,7 +52,6 @@ class ProductFiltersBuilder: def get_attribute_fitlers(self): attributes = [row.attribute for row in self.doc.filter_attributes] - print('ATTRIBUTES', attributes) attribute_docs = [ frappe.get_doc('Item Attribute', attribute) for attribute in attributes ] diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index ab19b77ad8..1088b4127d 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -17,7 +17,7 @@ class Bin(Document): '''Called from erpnext.stock.utils.update_bin''' self.update_qty(args) if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation": - from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle + from erpnext.stock.stock_ledger import update_entries_after, validate_negative_qty_in_future_sle if not args.get("posting_date"): args["posting_date"] = nowdate() @@ -37,8 +37,8 @@ class Bin(Document): "sle_id": args.name }, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher) - # Update qty_after_transaction in future SLEs of this item and warehouse - update_qty_in_future_sle(args) + # Validate negative qty in future transactions + validate_negative_qty_in_future_sle(args) def update_qty(self, args): # update the stock values (for current quantities) diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js index e429cd5e30..b3e4286bcc 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js @@ -31,7 +31,7 @@ frappe.ui.form.on('Repost Item Valuation', { } }, refresh: function(frm) { - if (frm.doc.status == "Failed") { + if (frm.doc.status == "Failed" && frm.doc.docstatus==1) { frm.add_custom_button(__('Restart'), function () { frm.trigger("restart_reposting"); }).addClass("btn-primary"); diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 2b2a7a202d..46919c8c8c 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -62,7 +62,7 @@ def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False): sle.submit() return sle -def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=False, via_landed_cost_voucher=False): +def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=None, via_landed_cost_voucher=False): if not args and voucher_type and voucher_no: args = get_args_for_voucher(voucher_type, voucher_no) @@ -181,7 +181,7 @@ class update_entries_after(object): self.process_sle(sle) if sle.dependant_sle_voucher_detail_no: - self.get_dependent_entries_to_fix(entries_to_fix, sle) + entries_to_fix = self.get_dependent_entries_to_fix(entries_to_fix, sle) if self.exceptions: self.raise_exceptions() @@ -221,13 +221,15 @@ class update_entries_after(object): excluded_sle=sle.name) if not dependant_sle: - return + return entries_to_fix elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse: - return - elif dependant_sle.item_code != self.item_code \ - and (dependant_sle.item_code, dependant_sle.warehouse) not in self.new_items: - self.new_items[(dependant_sle.item_code, dependant_sle.warehouse)] = dependant_sle - return + return entries_to_fix + elif dependant_sle.item_code != self.item_code: + if (dependant_sle.item_code, dependant_sle.warehouse) not in self.new_items: + self.new_items[(dependant_sle.item_code, dependant_sle.warehouse)] = dependant_sle + return entries_to_fix + elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse in self.data: + return entries_to_fix self.initialize_previous_data(dependant_sle) @@ -236,7 +238,7 @@ class update_entries_after(object): future_sle_for_dependant = list(self.get_sle_after_datetime(args)) entries_to_fix.extend(future_sle_for_dependant) - entries_to_fix = sorted(entries_to_fix, key=lambda k: k['timestamp']) + return sorted(entries_to_fix, key=lambda k: k['timestamp']) def process_sle(self, sle): # previous sle data for this warehouse @@ -612,11 +614,11 @@ class update_entries_after(object): frappe.local.flags.currently_saving): msg = _("{0} units of {1} needed in {2} to complete this transaction.").format( - abs(deficiency), frappe.get_desk_link('Item', self.item_code), + abs(deficiency), frappe.get_desk_link('Item', exceptions[0]["item_code"]), frappe.get_desk_link('Warehouse', warehouse)) else: msg = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format( - abs(deficiency), frappe.get_desk_link('Item', self.item_code), + abs(deficiency), frappe.get_desk_link('Item', exceptions[0]["item_code"]), frappe.get_desk_link('Warehouse', warehouse), exceptions[0]["posting_date"], exceptions[0]["posting_time"], frappe.get_desk_link(exceptions[0]["voucher_type"], exceptions[0]["voucher_no"])) @@ -761,25 +763,6 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, return valuation_rate -def update_qty_in_future_sle(args, allow_negative_stock=None): - frappe.db.sql(""" - update `tabStock Ledger Entry` - set qty_after_transaction = qty_after_transaction + {qty} - where - item_code = %(item_code)s - and warehouse = %(warehouse)s - and voucher_no != %(voucher_no)s - and is_cancelled = 0 - and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s) - or ( - timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s) - and creation > %(creation)s - ) - ) - """.format(qty=args.actual_qty), args) - - validate_negative_qty_in_future_sle(args, allow_negative_stock) - def validate_negative_qty_in_future_sle(args, allow_negative_stock=None): allow_negative_stock = allow_negative_stock \ or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) @@ -808,6 +791,7 @@ def get_future_sle_with_negative_qty(args): and voucher_no != %(voucher_no)s and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s) and is_cancelled = 0 - and qty_after_transaction < 0 + and qty_after_transaction + {0} < 0 + order by timestamp(posting_date, posting_time) asc limit 1 - """, args, as_dict=1) \ No newline at end of file + """.format(args.actual_qty), args, as_dict=1) \ No newline at end of file diff --git a/erpnext/support/report/issue_analytics/issue_analytics.py b/erpnext/support/report/issue_analytics/issue_analytics.py index 0b629151a6..3fdb10ddf3 100644 --- a/erpnext/support/report/issue_analytics/issue_analytics.py +++ b/erpnext/support/report/issue_analytics/issue_analytics.py @@ -147,8 +147,7 @@ class IssueAnalytics(object): self.entries = frappe.db.get_all('Issue', fields=[self.field_map.get(self.filters.based_on), 'name', 'opening_date'], - filters=filters, - debug=1 + filters=filters ) def get_common_filters(self): diff --git a/erpnext/support/report/issue_analytics/test_issue_analytics.py b/erpnext/support/report/issue_analytics/test_issue_analytics.py index 432906db9b..77483198ec 100644 --- a/erpnext/support/report/issue_analytics/test_issue_analytics.py +++ b/erpnext/support/report/issue_analytics/test_issue_analytics.py @@ -17,9 +17,12 @@ class TestIssueAnalytics(unittest.TestCase): current_month_date = getdate() last_month_date = add_months(current_month_date, -1) - self.current_month = str(months[current_month_date.month - 1]).lower() + '_' + str(current_month_date.year) - self.last_month = str(months[last_month_date.month - 1]).lower() + '_' + str(last_month_date.year) - + self.current_month = str(months[current_month_date.month - 1]).lower() + self.last_month = str(months[last_month_date.month - 1]).lower() + if current_month_date.year != last_month_date.year: + self.current_month += '_' + str(current_month_date.year) + self.last_month += '_' + str(last_month_date.year) + def test_issue_analytics(self): create_service_level_agreements_for_issues() create_issue_types() diff --git a/erpnext/www/all-products/index.py b/erpnext/www/all-products/index.py index 0622270e05..fd6400f48c 100644 --- a/erpnext/www/all-products/index.py +++ b/erpnext/www/all-products/index.py @@ -12,7 +12,6 @@ def get_context(context): search = frappe.form_dict.search field_filters = frappe.parse_json(frappe.form_dict.field_filters) attribute_filters = frappe.parse_json(frappe.form_dict.attribute_filters) - print(field_filters, attribute_filters) start = frappe.parse_json(frappe.form_dict.start) else: search = field_filters = attribute_filters = None @@ -30,8 +29,6 @@ def get_context(context): context.field_filters = filter_engine.get_field_filters() context.attribute_filters = filter_engine.get_attribute_fitlers() - print(context.field_filters, context.attribute_filters) - context.product_settings = product_settings context.body_class = "product-page" context.page_length = product_settings.products_per_page or 20 From 0701f08ab6742a679637b7958acf9d75d1c9166e Mon Sep 17 00:00:00 2001 From: pateljannat Date: Wed, 3 Feb 2021 11:53:14 +0530 Subject: [PATCH 623/728] fix: item_group on update items --- erpnext/controllers/accounts_controller.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 21874fe2ca..919fdb1bc7 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1319,6 +1319,7 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description + child_item.item_group = item.item_group child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date child_item.uom = trans_item.get("uom") or item.stock_uom conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) @@ -1342,6 +1343,7 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description + child_item.item_group = item.item_group child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date child_item.uom = trans_item.get("uom") or item.stock_uom conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) From dce73d1881ec8f79bf3ac0376f8935a0e7122107 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 3 Feb 2021 12:06:36 +0530 Subject: [PATCH 624/728] fix: Add accounts user role permission for accounting dimension filter --- .../accounting_dimension_filter.json | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json index c0327ad0ad..0f3fbc0b8d 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json @@ -108,7 +108,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2020-12-16 15:27:23.659285", + "modified": "2021-02-03 12:04:58.678402", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting Dimension Filter", @@ -125,6 +125,30 @@ "role": "System Manager", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 } ], "quick_entry": 1, From 8c3da162baef22ce50ed27fd18035b6de96e2a22 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 4 Feb 2021 19:39:41 +0530 Subject: [PATCH 625/728] feat: introduce parameter group in quality inspection --- .../item_quality_inspection_parameter.json | 11 ++- .../quality_inspection_parameter.json | 13 ++- .../__init__.py | 0 .../quality_inspection_parameter_group.js | 8 ++ .../quality_inspection_parameter_group.json | 82 +++++++++++++++++++ .../quality_inspection_parameter_group.py | 10 +++ ...test_quality_inspection_parameter_group.py | 10 +++ .../quality_inspection_reading.json | 11 ++- 8 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 erpnext/stock/doctype/quality_inspection_parameter_group/__init__.py create mode 100644 erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js create mode 100644 erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.json create mode 100644 erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py create mode 100644 erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py 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 471e6853b5..9b1a47eed6 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 @@ -7,6 +7,7 @@ "engine": "InnoDB", "field_order": [ "specification", + "parameter_group", "value", "numeric", "column_break_3", @@ -75,12 +76,20 @@ "in_list_view": 1, "label": "Numeric", "width": "80px" + }, + { + "fetch_from": "specification.parameter_group", + "fieldname": "parameter_group", + "fieldtype": "Link", + "label": "Parameter Group", + "options": "Quality Inspection Parameter Group", + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-02-01 19:18:46.924399", + "modified": "2021-02-04 18:50:02.056173", "modified_by": "Administrator", "module": "Stock", "name": "Item Quality Inspection Parameter", diff --git a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json index 0b5a9b5b3c..6d97601091 100644 --- a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json +++ b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json @@ -7,24 +7,35 @@ "engine": "InnoDB", "field_order": [ "parameter", + "parameter_group", "description" ], "fields": [ { "fieldname": "parameter", "fieldtype": "Data", + "in_list_view": 1, "label": "Parameter", + "reqd": 1, "unique": 1 }, { "fieldname": "description", "fieldtype": "Text Editor", "label": "Description" + }, + { + "fieldname": "parameter_group", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Parameter Group", + "options": "Quality Inspection Parameter Group", + "reqd": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2020-12-28 18:06:54.897317", + "modified": "2021-02-04 18:45:12.486344", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection Parameter", diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/__init__.py b/erpnext/stock/doctype/quality_inspection_parameter_group/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js new file mode 100644 index 0000000000..8716a29871 --- /dev/null +++ b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Quality Inspection Parameter Group', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.json b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.json new file mode 100644 index 0000000000..57264741a6 --- /dev/null +++ b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.json @@ -0,0 +1,82 @@ +{ + "actions": [], + "autoname": "field:group_name", + "creation": "2021-02-04 18:44:12.223295", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "group_name" + ], + "fields": [ + { + "fieldname": "group_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Parameter Group Name", + "reqd": 1, + "unique": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-02-04 18:44:12.223295", + "modified_by": "Administrator", + "module": "Stock", + "name": "Quality Inspection Parameter Group", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py new file mode 100644 index 0000000000..1a3b1a0463 --- /dev/null +++ b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, 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 QualityInspectionParameterGroup(Document): + pass diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py b/erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py new file mode 100644 index 0000000000..212d4b8c21 --- /dev/null +++ b/erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestQualityInspectionParameterGroup(unittest.TestCase): + pass 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 35d58eff58..0eff5a8f00 100644 --- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json +++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json @@ -7,6 +7,7 @@ "engine": "InnoDB", "field_order": [ "specification", + "parameter_group", "status", "value", "numeric", @@ -210,12 +211,20 @@ "fieldtype": "Check", "in_list_view": 1, "label": "Numeric" + }, + { + "fetch_from": "specification.parameter_group", + "fieldname": "parameter_group", + "fieldtype": "Link", + "label": "Parameter Group", + "options": "Quality Inspection Parameter Group", + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-02-01 19:46:22.138018", + "modified": "2021-02-04 19:15:37.991221", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection Reading", From b814b002cecb1fd8f257d16c36f022ecb1cae091 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Fri, 5 Feb 2021 11:34:55 +0530 Subject: [PATCH 626/728] fix: creating salary slip from timesheet (#24541) --- erpnext/projects/doctype/timesheet/timesheet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 9e807f728e..ea81b3eb64 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -288,7 +288,7 @@ def make_sales_invoice(source_name, item_code=None, customer=None): def make_salary_slip(source_name, target_doc=None): target = frappe.new_doc("Salary Slip") set_missing_values(source_name, target) - target.run_method("get_emp_and_leave_details") + target.run_method("get_emp_and_working_day_details") return target From a7820f310ca4efd59a7d585c6fa7567797579ecb Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 5 Feb 2021 14:52:31 +0530 Subject: [PATCH 627/728] fix: check if product_info exists --- erpnext/shopping_cart/product_query.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/shopping_cart/product_query.py b/erpnext/shopping_cart/product_query.py index da9e798327..8daf3d7dac 100644 --- a/erpnext/shopping_cart/product_query.py +++ b/erpnext/shopping_cart/product_query.py @@ -73,7 +73,8 @@ class ProductQuery: for item in result: product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get('product_info') - item.formatted_price = product_info['price'].get('formatted_price') if product_info['price'] else None + if product_info: + item.formatted_price = product_info['price'].get('formatted_price') if product_info['price'] else None return result From 91b41db9e374dc295bb1ae83f653d23ccadea5d4 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 5 Feb 2021 17:34:56 +0530 Subject: [PATCH 628/728] fix: check if product_info exists before setting price --- erpnext/portal/product_configurator/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 4693d44509..21fd7c2878 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -386,7 +386,8 @@ def get_items(filters=None, search=None): r.description = r.web_long_description or r.description r.image = r.website_image or r.image product_info = get_product_info_for_website(r.item_code, skip_quotation_creation=True).get('product_info') - r.formatted_price = product_info['price'].get('formatted_price') if product_info['price'] else None + if product_info: + r.formatted_price = product_info['price'].get('formatted_price') if product_info['price'] else None return results From c4a1bb0e1c1eddc747fe050ee05e625f8d0d7e8a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 5 Feb 2021 10:04:18 -0800 Subject: [PATCH 629/728] fix: couple of travis fixes (#24554) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- .../doctype/inpatient_record/test_inpatient_record.py | 1 + erpnext/hr/doctype/employee/test_employee.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py index 8a918b0275..ea0d1e982d 100644 --- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py @@ -139,6 +139,7 @@ def create_inpatient(patient): inpatient_record.phone = patient_obj.phone inpatient_record.inpatient = "Scheduled" inpatient_record.scheduled_date = today() + inpatient_record.company = "_Test Company" return inpatient_record diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py index f4b214adc3..c0e614ac08 100644 --- a/erpnext/hr/doctype/employee/test_employee.py +++ b/erpnext/hr/doctype/employee/test_employee.py @@ -16,11 +16,13 @@ class TestEmployee(unittest.TestCase): employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) employee.date_of_birth = "1992" + frappe.utils.nowdate()[4:] employee.company_email = "test@example.com" + employee.company = "_Test Company" employee.save() from erpnext.hr.doctype.employee.employee import get_employees_who_are_born_today, send_birthday_reminders - self.assertTrue(employee.name in [e.name for e in get_employees_who_are_born_today()]) + employees_born_today = get_employees_who_are_born_today() + self.assertTrue(employees_born_today.get("_Test Company")) frappe.db.sql("delete from `tabEmail Queue`") From ffb36aac8f604e88caa6c77951dea6fe33f30676 Mon Sep 17 00:00:00 2001 From: Anuja Pawar <60467153+Anuja-pawar@users.noreply.github.com> Date: Fri, 5 Feb 2021 23:35:47 +0530 Subject: [PATCH 630/728] fix: fixing item_code not found error in the report (#24483) --- .../item_wise_sales_register/item_wise_sales_register.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 998003ac69..928b373eff 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -52,8 +52,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum row = { 'item_code': d.item_code, - 'item_name': item_record.item_name, - 'item_group': item_record.item_group, + 'item_name': item_record.item_name if item_record else d.item_name, + 'item_group': item_record.item_group if item_record else d.item_group, 'description': d.description, 'invoice': d.parent, 'posting_date': d.posting_date, @@ -383,6 +383,7 @@ def get_items(filters, additional_query_columns): `tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks, `tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total, `tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description, + `tabSales Invoice Item`.`item_name`, `tabSales Invoice Item`.`item_group`, `tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center, `tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom, From f29b811a565ea619f12895ef41f187dfc81a6dbc Mon Sep 17 00:00:00 2001 From: Saqib Date: Sat, 6 Feb 2021 17:55:20 +0530 Subject: [PATCH 631/728] fix(e-invoice): do not validate gstin for exports (#24561) --- erpnext/regional/india/e_invoice/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index c8e7e149c4..7d9b2fd624 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -303,7 +303,7 @@ def validate_mandatory_fields(invoice): _('GSTIN is mandatory to fetch company GSTIN details. Please enter GSTIN in selected company address.'), title=_('Missing Fields') ) - if not frappe.db.get_value('Address', invoice.customer_address, 'gstin'): + if invoice.gst_category != 'Overseas' and not frappe.db.get_value('Address', invoice.customer_address, 'gstin'): frappe.throw( _('GSTIN is mandatory to fetch customer GSTIN details. Please enter GSTIN in selected customer address.'), title=_('Missing Fields') From a97eb6a052b605ef06137e128eed66052bdf4965 Mon Sep 17 00:00:00 2001 From: marination Date: Sun, 7 Feb 2021 22:27:40 +0530 Subject: [PATCH 632/728] fix: Empty State text font weight in Item Dashboard --- erpnext/stock/dashboard/item_dashboard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index 9dbb64c343..95cb92b1b3 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -132,7 +132,7 @@ erpnext.stock.ItemDashboard = Class.extend({ var message = __("No Stock Available Currently"); this.content.find('.result').css('text-align', 'center'); - $(`
    + $(`
    ${message}
    `).appendTo(this.result); } }, From 9490122abb40ffff8d987c4bdb85d7aa38e92c1e Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 8 Feb 2021 11:40:56 +0530 Subject: [PATCH 633/728] fix(e-invoice): skip e-invoice generation for non-taxable invoices (#24568) --- erpnext/regional/india/e_invoice/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index 7d9b2fd624..5b898d52a1 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -20,11 +20,13 @@ from frappe.utils.data import cstr, cint, format_date, flt, time_diff_in_seconds def validate_einvoice_fields(doc): einvoicing_enabled = cint(frappe.db.get_value('E Invoice Settings', 'E Invoice Settings', 'enable')) - invalid_doctype = doc.doctype not in ['Sales Invoice'] + invalid_doctype = doc.doctype != 'Sales Invoice' invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export'] company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin') + no_taxes_applied = len(doc.get('taxes')) == 0 - if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction: return + if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction or no_taxes_applied: + return if doc.docstatus == 0 and doc._action == 'save': if doc.irn: From 53dbe39d12c955df7cfd8d967fa0c077a8c49042 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Mon, 8 Feb 2021 12:32:42 +0530 Subject: [PATCH 634/728] fix: StopIteration error when e-invoice not enabled (#24548) * fix: StopIteration error when e-invoice not enabled * chore: update message Co-authored-by: Saqib --- erpnext/regional/india/e_invoice/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index 5b898d52a1..e64be4b645 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -446,6 +446,8 @@ class GSPConnector(): def get_credentials(self): if self.invoice: gstin = self.get_seller_gstin() + if not self.e_invoice_settings.enable: + frappe.throw(_("E-Invoicing is disabled. Please enable it from {} to generate e-invoices.").format(get_link_to_form("E Invoice Settings", "E Invoice Settings"))) credentials = next(d for d in self.e_invoice_settings.credentials if d.gstin == gstin) else: credentials = self.e_invoice_settings.credentials[0] if self.e_invoice_settings.credentials else None @@ -819,4 +821,4 @@ def generate_eway_bill(doctype, docname, **kwargs): @frappe.whitelist() def cancel_eway_bill(doctype, docname, eway_bill, reason, remark): gsp_connector = GSPConnector(doctype, docname) - gsp_connector.cancel_eway_bill(eway_bill, reason, remark) \ No newline at end of file + gsp_connector.cancel_eway_bill(eway_bill, reason, remark) From 5a7ddba9a319f666a348816beea843a8a99bd3a0 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 8 Feb 2021 17:06:23 +0530 Subject: [PATCH 635/728] fix: Avoid changing Ref. Doctype in Accounting Dimension after creation --- .../accounting_dimension/accounting_dimension.json | 3 ++- .../accounting_dimension/accounting_dimension.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json index cf55d554fb..5858f10bb0 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json @@ -30,6 +30,7 @@ "fieldtype": "Link", "label": "Reference Document Type", "options": "DocType", + "read_only_depends_on": "eval:!doc.__islocal", "reqd": 1 }, { @@ -48,7 +49,7 @@ } ], "links": [], - "modified": "2020-03-22 20:34:39.805728", + "modified": "2021-02-08 16:37:53.936656", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting Dimension", diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index ef0d3a3cb1..dd26c4cec2 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -29,6 +29,16 @@ class AccountingDimension(Document): if exists and self.is_new(): frappe.throw("Document Type already used as a dimension") + if not self.is_new(): + self.validate_document_type_change() + + def validate_document_type_change(self): + doctype_before_save = frappe.db.get_value("Accounting Dimension", self.name, "document_type") + if doctype_before_save != self.document_type: + message = _("Cannot change Reference Document Type.") + message += _("Please create a new Accounting Dimension if required.") + frappe.throw(message) + def after_insert(self): if frappe.flags.in_test: make_dimension_in_accounting_doctypes(doc=self) From c4d67c2afeae8916de364a68d2a763150c589bea Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 8 Feb 2021 18:42:27 +0530 Subject: [PATCH 636/728] refactor: Move call_popup style from less to scss - Add style for audio --- erpnext/public/build.json | 2 +- erpnext/public/less/call_popup.less | 9 --------- erpnext/public/scss/call_popup.scss | 21 +++++++++++++++++++++ 3 files changed, 22 insertions(+), 10 deletions(-) delete mode 100644 erpnext/public/less/call_popup.less create mode 100644 erpnext/public/scss/call_popup.scss diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 4a40e8e8f9..7326238273 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -2,7 +2,7 @@ "css/erpnext.css": [ "public/less/erpnext.less", "public/less/hub.less", - "public/less/call_popup.less", + "public/scss/call_popup.scss", "public/scss/point-of-sale.scss" ], "css/marketplace.css": [ diff --git a/erpnext/public/less/call_popup.less b/erpnext/public/less/call_popup.less deleted file mode 100644 index 32e85ce16d..0000000000 --- a/erpnext/public/less/call_popup.less +++ /dev/null @@ -1,9 +0,0 @@ -.call-popup { - a:hover { - text-decoration: underline; - } - .for-description { - max-height: 250px; - overflow: scroll; - } -} \ No newline at end of file diff --git a/erpnext/public/scss/call_popup.scss b/erpnext/public/scss/call_popup.scss new file mode 100644 index 0000000000..95e31828c1 --- /dev/null +++ b/erpnext/public/scss/call_popup.scss @@ -0,0 +1,21 @@ +.call-popup { + a:hover { + text-decoration: underline; + } + .for-description { + max-height: 250px; + overflow: scroll; + } +} + +audio { + height: 40px; + width: 100%; + max-width: 500px; + background-color: var(--control-bg); + border-radius: var(--border-radius-sm); + &-webkit-media-controls-panel { + background: var(--control-bg); + } + outline: none; +} From 723e6dd73fb76dece4ada93f4b5d4122f8bf28db Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 8 Feb 2021 18:43:26 +0530 Subject: [PATCH 637/728] fix: Update style of call log shown in timeline --- erpnext/public/js/templates/call_link.html | 39 +++++++++---------- .../telephony/doctype/call_log/call_log.py | 2 + 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/erpnext/public/js/templates/call_link.html b/erpnext/public/js/templates/call_link.html index 08bdf142a7..071078c776 100644 --- a/erpnext/public/js/templates/call_link.html +++ b/erpnext/public/js/templates/call_link.html @@ -1,32 +1,31 @@
    -
    -
    +
    +
    + {{ type }} Call + {% if (duration) %} + • {{ frappe.format(duration, { fieldtype: "Duration" }) }} + {% endif %} + • {{ comment_when(creation) }} +
    - - - {{ type }} Call - - - {{ frappe.format(duration, { fieldtype: "Duration" }) }} - - - {{ comment_when(creation) }} - - - - Details - {% if (show_call_button) { %} - Callback - {% } %} + + + + + +
    -
    + + +
    {% if (type === "Incoming") { %} Incoming call from {{ from }}, received by {{ to }} {% } else { %} Outgoing Call made by {{ from }} to {{ to }} {% } %} -
    -
    +
    {% if (summary) { %} - {{ summary }} + {{ summary }} {% } else { %} {{ __("No Summary") }} {% } %} diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index a277a5f956..4d553df08b 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -165,6 +165,8 @@ def get_linked_call_logs(doctype, docname): for log in logs: log.show_call_button = 0 timeline_contents.append({ + 'icon': 'call', + 'is_card': True, 'creation': log.creation, 'template': 'call_link', 'template_data': log From ba26020a824436545b933d1469f4de07fe72f06a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 8 Feb 2021 18:45:55 +0530 Subject: [PATCH 638/728] fix: Show audio interface in call log which has recording URL - Also, remove unneccesary column & section break in call log doctype --- .../telephony/doctype/call_log/call_log.js | 25 +++++++++++++++-- .../telephony/doctype/call_log/call_log.json | 28 ++++++++----------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/erpnext/telephony/doctype/call_log/call_log.js b/erpnext/telephony/doctype/call_log/call_log.js index 977f86da0d..e7afa0b7d0 100644 --- a/erpnext/telephony/doctype/call_log/call_log.js +++ b/erpnext/telephony/doctype/call_log/call_log.js @@ -2,7 +2,26 @@ // For license information, please see license.txt frappe.ui.form.on('Call Log', { - // refresh: function(frm) { - - // } + refresh: function(frm) { + frm.events.setup_recording_audio_control(frm); + const incoming_call = frm.doc.type == 'Incoming'; + frm.add_custom_button(incoming_call ? __('Callback'): __('Call Again'), () => { + const number = incoming_call ? frm.doc.from : frm.doc.to; + frappe.phone_call.handler(number, frm); + }); + }, + setup_recording_audio_control(frm) { + const recording_wrapper = frm.get_field('recording_html').$wrapper; + if (!frm.doc.recording_url || frm.doc.recording_url == 'null') { + recording_wrapper.empty(); + } else { + recording_wrapper.addClass('input-max-width'); + recording_wrapper.html(` + + `); + } + } }); diff --git a/erpnext/telephony/doctype/call_log/call_log.json b/erpnext/telephony/doctype/call_log/call_log.json index 1ecd884bbd..1d6c39edf6 100644 --- a/erpnext/telephony/doctype/call_log/call_log.json +++ b/erpnext/telephony/doctype/call_log/call_log.json @@ -5,6 +5,7 @@ "doctype": "DocType", "engine": "InnoDB", "field_order": [ + "call_details_section", "id", "from", "to", @@ -21,20 +22,9 @@ "section_break_11", "summary", "section_break_19", - "links", - "column_break_3", - "section_break_5" + "links" ], "fields": [ - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "label": "Call Details" - }, { "fieldname": "id", "fieldtype": "Data", @@ -75,6 +65,7 @@ { "fieldname": "recording_url", "fieldtype": "Data", + "hidden": 1, "label": "Recording URL" }, { @@ -112,13 +103,13 @@ }, { "fieldname": "summary", - "fieldtype": "Small Text", - "label": "Call Summary" + "fieldtype": "Small Text" }, { "fieldname": "section_break_11", "fieldtype": "Section Break", - "hide_border": 1 + "hide_border": 1, + "label": "Call Summary" }, { "fieldname": "start_time", @@ -138,12 +129,17 @@ "label": "Customer", "options": "Customer", "read_only": 1 + }, + { + "fieldname": "call_details_section", + "fieldtype": "Section Break", + "label": "Call Details" } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2021-01-13 12:28:20.288985", + "modified": "2021-02-08 14:23:28.744844", "modified_by": "Administrator", "module": "Telephony", "name": "Call Log", From c5248b30ebda26bd89fece350de6a9f11ec95879 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 8 Feb 2021 18:50:05 +0530 Subject: [PATCH 639/728] fix: Do not re-use call popup if call ID is different --- erpnext/public/js/call_popup/call_popup.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index be1745e54f..f3d09ee998 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -191,11 +191,12 @@ class CallPopup { $(document).on('app_ready', function () { frappe.realtime.on('show_call_popup', call_log => { - if (!erpnext.call_popup) { + let call_popup = erpnext.call_popup; + if (call_popup && call_log.name === call_popup.call_log.name) { erpnext.call_popup = new CallPopup(call_log); } else { - erpnext.call_popup.update_call_log(call_log); - erpnext.call_popup.dialog.show(); + call_popup.update_call_log(call_log); + call_popup.dialog.show(); } }); }); From dcc9947d10d0810b9b61028f98431cf95d5a32cb Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 8 Feb 2021 20:13:23 +0530 Subject: [PATCH 640/728] fix: Update call_popup - Setup missed & call ended listeners - Show contacts based on links --- erpnext/crm/doctype/lead/lead.py | 4 +- erpnext/public/js/call_popup/call_popup.js | 237 +++++++++++---------- 2 files changed, 125 insertions(+), 116 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 938cbfdc85..d1d096843b 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -352,7 +352,7 @@ def get_lead_with_phone_number(number): leads = frappe.get_all('Lead', or_filters={ 'phone': ['like', '%{}'.format(number)], 'mobile_no': ['like', '%{}'.format(number)] - }, limit=1) + }, limit=1, order_by="creation DESC") lead = leads[0].name if leads else None @@ -361,4 +361,4 @@ def get_lead_with_phone_number(number): def daily_open_lead(): leads = frappe.get_all("Lead", filters = [["contact_date", "Between", [nowdate(), nowdate()]]]) for lead in leads: - frappe.db.set_value("Lead", lead.name, "status", "Open") \ No newline at end of file + frappe.db.set_value("Lead", lead.name, "status", "Open") diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index f3d09ee998..24297885a2 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -7,10 +7,103 @@ class CallPopup { } make() { + frappe.utils.play_sound('incoming-call'); this.dialog = new frappe.ui.Dialog({ 'static': true, - 'minimizable': true, - 'fields': [{ + 'minimizable': true + }); + this.dialog.get_close_btn().show(); + this.setup_dialog(); + this.set_call_status(); + frappe.utils.bind_actions_with_object(this.dialog.$body, this); + this.dialog.$wrapper.addClass('call-popup'); + this.dialog.get_close_btn().unbind('click').click(this.close_modal.bind(this)); + this.dialog.show(); + } + + setup_dialog() { + this.setup_call_details(); + this.dialog.$body.empty().append(this.caller_info); + } + + set_indicator(color, blink=false) { + let classes = `indicator ${color} ${blink ? 'blink': ''}`; + this.dialog.header.find('.indicator').attr('class', classes); + } + + set_call_status(call_status) { + let title = ''; + call_status = call_status || this.call_log.status; + if (['Ringing'].includes(call_status) || !call_status) { + title = __('Incoming call from {0}', [this.get_caller_name() || this.caller_number]); + this.set_indicator('blue', true); + } else if (call_status === 'In Progress') { + title = __('Call Connected'); + this.set_indicator('green'); + } else if (['No Answer', 'Missed'].includes(call_status)) { + this.set_indicator('yellow'); + title = __('Call Missed'); + } else if (['Completed', 'Busy', 'Failed'].includes(call_status)) { + this.set_indicator('red'); + title = __('Call Ended'); + } else { + this.set_indicator('blue'); + title = call_status; + } + this.dialog.set_title(title); + } + + update_call_log(call_log, missed) { + this.call_log = call_log; + this.set_call_status(missed ? 'Missed': null); + } + + close_modal() { + this.dialog.hide(); + delete erpnext.call_popup; + } + + call_ended(call_log, missed) { + frappe.utils.play_sound('call-disconnect'); + this.update_call_log(call_log, missed); + setTimeout(() => { + if (!this.dialog.get_value('call_summary')) { + this.close_modal(); + } + }, 60000); + this.clear_listeners(); + } + + get_caller_name() { + const contact_link = this.get_contact_link(); + return contact_link.link_title || contact_link.link_name; + } + + get_contact_link() { + let log = this.call_log; + let contact_link = log.links.find(d => d.link_doctype === 'Contact'); + return contact_link || {}; + } + + setup_listener() { + frappe.realtime.on(`call_${this.call_log.id}_ended`, call_log => { + this.call_ended(call_log); + }); + + frappe.realtime.on(`call_${this.call_log.id}_missed`, call_log => { + this.call_ended(call_log, true); + }); + } + + clear_listeners() { + frappe.realtime.off(`call_${this.call_log.id}_ended`); + frappe.realtime.off(`call_${this.call_log.id}_missed`); + } + + setup_call_details() { + this.caller_info = $(`
    `); + this.call_details = new frappe.ui.FieldGroup({ + fields: [{ 'fieldname': 'name', 'label': 'Name', 'default': this.get_caller_name() || __('Unknown Caller'), @@ -19,17 +112,17 @@ class CallPopup { }, { 'fieldtype': 'Button', 'label': __('Open Contact'), - 'click': () => frappe.set_route('Form', 'Contact', this.call_log.contact), - 'depends_on': () => this.call_log.contact - }, { - 'fieldtype': 'Button', - 'label': __('Open Lead'), - 'click': () => frappe.set_route('Form', 'Lead', this.call_log.lead), - 'depends_on': () => this.call_log.lead + 'click': () => frappe.set_route('Form', 'Contact', this.get_contact_link().link_name), + 'depends_on': () => this.get_caller_name() }, { 'fieldtype': 'Button', 'label': __('Create New Contact'), - 'click': () => frappe.new_doc('Contact', { 'mobile_no': this.caller_number }), + 'click': this.create_new_contact.bind(this), + 'depends_on': () => !this.get_caller_name() + }, { + 'fieldtype': 'Button', + 'label': __('Create New Customer'), + 'click': this.create_new_customer.bind(this), 'depends_on': () => !this.get_caller_name() }, { 'fieldtype': 'Button', @@ -44,26 +137,9 @@ class CallPopup { 'fieldtype': 'Data', 'default': this.caller_number, 'read_only': 1 - }, { - 'fielname': 'last_interaction', - 'fieldtype': 'Section Break', - 'label': __('Activity'), - 'depends_on': () => this.get_caller_name() - }, { - 'fieldtype': 'Small Text', - 'label': __('Last Issue'), - 'fieldname': 'last_issue', - 'read_only': true, - 'depends_on': () => this.call_log.contact, - 'default': `${__('No issue has been raised by the caller.')}` - }, { - 'fieldtype': 'Small Text', - 'label': __('Last Communication'), - 'fieldname': 'last_communication', - 'read_only': true, - 'default': `${__('No communication found.')}` }, { 'fieldtype': 'Section Break', + 'hide_border': 1, }, { 'fieldtype': 'Small Text', 'label': __('Call Summary'), @@ -72,7 +148,7 @@ class CallPopup { 'fieldtype': 'Button', 'label': __('Save'), 'click': () => { - const call_summary = this.dialog.get_value('call_summary'); + const call_summary = this.call_details.get_value('call_summary'); if (!call_summary) return; frappe.xcall('erpnext.telephony.doctype.call_log.call_log.add_call_summary', { 'call_log': this.call_log.name, @@ -94,98 +170,29 @@ class CallPopup { }); } }], + body: this.caller_info }); - this.set_call_status(); - this.dialog.get_close_btn().show(); - this.make_last_interaction_section(); - this.dialog.$body.addClass('call-popup'); - this.dialog.set_secondary_action(this.close_modal.bind(this)); - frappe.utils.play_sound('incoming-call'); - this.dialog.show(); + this.call_details.make(); } - set_indicator(color, blink=false) { - let classes = `indicator ${color} ${blink ? 'blink': ''}`; - this.dialog.header.find('.indicator').attr('class', classes); + is_known_caller() { + return Boolean(this.get_caller_name()); } - set_call_status(call_status) { - let title = ''; - call_status = call_status || this.call_log.status; - if (['Ringing'].includes(call_status) || !call_status) { - title = __('Incoming call from {0}', [this.get_caller_name() || this.caller_number]); - this.set_indicator('blue', true); - } else if (call_status === 'In Progress') { - title = __('Call Connected'); - this.set_indicator('yellow'); - } else if (call_status === 'Missed') { - this.set_indicator('red'); - title = __('Call Missed'); - } else if (['Completed', 'Disconnected'].includes(call_status)) { - this.set_indicator('red'); - title = __('Call Disconnected'); - } else { - this.set_indicator('blue'); - title = call_status; - } - this.dialog.set_title(title); + create_new_customer() { + // to avoid quick entry form + const new_customer = frappe.model.get_new_doc('Customer'); + new_customer.mobile_no = this.caller_number; + frappe.set_route('Form', new_customer.doctype, new_customer.name); } - update_call_log(call_log) { - this.call_log = call_log; - this.set_call_status(); - } - - close_modal() { - this.dialog.hide(); - delete erpnext.call_popup; - } - - call_disconnected(call_log) { - frappe.utils.play_sound('call-disconnect'); - this.update_call_log(call_log); - setTimeout(() => { - if (!this.dialog.get_value('call_summary')) { - this.close_modal(); - } - }, 30000); - } - - make_last_interaction_section() { - frappe.xcall('erpnext.crm.doctype.utils.get_last_interaction', { - 'contact': this.call_log.contact, - 'lead': this.call_log.lead - }).then(data => { - const comm_field = this.dialog.get_field('last_communication'); - if (data.last_communication) { - const comm = data.last_communication; - comm_field.set_value(comm.content); - } - - if (data.last_issue) { - const issue = data.last_issue; - 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])} - - `); - } - }); - } - - get_caller_name() { - let log = this.call_log; - return log.contact_name || log.lead_name; - } - - setup_listener() { - frappe.realtime.on(`call_${this.call_log.id}_disconnected`, call_log => { - this.call_disconnected(call_log); - // Remove call disconnect listener after the call is disconnected - frappe.realtime.off(`call_${this.call_log.id}_disconnected`); - }); + create_new_contact() { + // TODO: fix new_doc, it should accept child table values + const new_contact = frappe.model.get_new_doc('Contact'); + const phone_no = frappe.model.add_child(new_contact, 'Contact Phone', 'phone_nos'); + phone_no.phone = this.caller_number; + phone_no.is_primary_mobile_no = 1; + frappe.set_route('Form', new_contact.doctype, new_contact.name); } } @@ -200,3 +207,5 @@ $(document).on('app_ready', function () { } }); }); + +window.CallPopup = CallPopup; From 8991d4ed0b9a67e282063e2e8ba18de82e8e043d Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 8 Feb 2021 20:19:11 +0530 Subject: [PATCH 641/728] fix: fetching of standalone cr/dr notes for reconciliation (#24575) --- .../payment_reconciliation.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 791b03a0d7..f7a15c04fa 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -88,19 +88,19 @@ class PaymentReconciliation(Document): voucher_type = ('Sales Invoice' if self.party_type == 'Customer' else "Purchase Invoice") - return frappe.db.sql(""" SELECT `tab{doc}`.name as reference_name, %(voucher_type)s as reference_type, - (sum(`tabGL Entry`.{dr_or_cr}) - sum(`tabGL Entry`.{reconciled_dr_or_cr})) as amount, + return frappe.db.sql(""" SELECT doc.name as reference_name, %(voucher_type)s as reference_type, + (sum(gl.{dr_or_cr}) - sum(gl.{reconciled_dr_or_cr})) as amount, account_currency as currency - FROM `tab{doc}`, `tabGL Entry` + FROM `tab{doc}` doc, `tabGL Entry` gl WHERE - (`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no) - and `tab{doc}`.{party_type_field} = %(party)s - and `tab{doc}`.is_return = 1 and `tab{doc}`.return_against IS NULL - and `tabGL Entry`.against_voucher_type = %(voucher_type)s - and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s - and `tabGL Entry`.party_type = %(party_type)s and `tabGL Entry`.account = %(account)s - and `tabGL Entry`.is_cancelled = 0 - GROUP BY `tab{doc}`.name + (doc.name = gl.against_voucher or doc.name = gl.voucher_no) + and doc.{party_type_field} = %(party)s + and doc.is_return = 1 and ifnull(doc.return_against, "") = "" + and gl.against_voucher_type = %(voucher_type)s + and doc.docstatus = 1 and gl.party = %(party)s + and gl.party_type = %(party_type)s and gl.account = %(account)s + and gl.is_cancelled = 0 + GROUP BY doc.name Having amount > 0 """.format( @@ -113,7 +113,7 @@ class PaymentReconciliation(Document): 'party_type': self.party_type, 'voucher_type': voucher_type, 'account': self.receivable_payable_account - }, as_dict=1) + }, as_dict=1, debug=1) def add_payment_entries(self, entries): self.set('payments', []) From 1016ff48fcd984c7e2e6be0d2d0d377ef021c4c0 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 9 Feb 2021 12:17:29 +0530 Subject: [PATCH 642/728] fix: Create new call popup if call popup does not exists --- erpnext/public/js/call_popup/call_popup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index 24297885a2..c954f12ac6 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -200,10 +200,10 @@ $(document).on('app_ready', function () { frappe.realtime.on('show_call_popup', call_log => { let call_popup = erpnext.call_popup; if (call_popup && call_log.name === call_popup.call_log.name) { - erpnext.call_popup = new CallPopup(call_log); - } else { call_popup.update_call_log(call_log); call_popup.dialog.show(); + } else { + erpnext.call_popup = new CallPopup(call_log); } }); }); From 637ddff6e9a24b2f4ae05f22230ba61199a0d2cb Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 9 Feb 2021 16:17:30 +0530 Subject: [PATCH 643/728] fix: code cleanup --- erpnext/controllers/accounts_controller.py | 53 +++++++--------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 919fdb1bc7..d341030aba 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1309,47 +1309,28 @@ def add_taxes_from_tax_template(child_item, parent_doc): }) tax_row.db_insert() -def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item): +def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, trans_item): """ - Returns a Sales Order Item child item containing the default values + Returns a Sales/Purchase Order Item child item containing the default values """ p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) - child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname) + child_item = frappe.new_doc(child_doctype, p_doc, child_docname) item = frappe.get_doc("Item", trans_item.get('item_code')) - child_item.item_code = item.item_code - child_item.item_name = item.item_name - child_item.description = item.description - child_item.item_group = item.item_group - child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date + for field in ("item_code", "item_name", "description", "item_group"): + child_item.update({field: item.get(field)}) + date_fieldname = "delivery_date" if child_doctype == "Sales Order Item" else "schedule_date" + child_item.date_fieldname = trans_item.get(date_fieldname) or p_doc.date_fieldname child_item.uom = trans_item.get("uom") or item.stock_uom conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor - set_child_tax_template_and_map(item, child_item, p_doc) - add_taxes_from_tax_template(child_item, p_doc) - child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) - if not child_item.warehouse: - frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") - .format(frappe.bold("default warehouse"), frappe.bold(item.item_code))) - return child_item - - -def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item): - """ - Returns a Purchase Order Item child item containing the default values - """ - p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) - child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname) - item = frappe.get_doc("Item", trans_item.get('item_code')) - child_item.item_code = item.item_code - child_item.item_name = item.item_name - child_item.description = item.description - child_item.item_group = item.item_group - child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date - child_item.uom = trans_item.get("uom") or item.stock_uom - conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) - child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor - child_item.base_rate = 1 # Initiallize value will update in parent validation - child_item.base_amount = 1 # Initiallize value will update in parent validation + if child_doctype == "Purchase Order Item": + child_item.base_rate = 1 # Initiallize value will update in parent validation + child_item.base_amount = 1 # Initiallize value will update in parent validation + if child_doctype == "Sales Order Item": + child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) + if not child_item.warehouse: + frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") + .format(frappe.bold("default warehouse"), frappe.bold(item.item_code))) set_child_tax_template_and_map(item, child_item, p_doc) add_taxes_from_tax_template(child_item, p_doc) return child_item @@ -1413,8 +1394,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil ) def get_new_child_item(item_row): - new_child_function = set_sales_order_defaults if parent_doctype == "Sales Order" else set_purchase_order_defaults - return new_child_function(parent_doctype, parent_doctype_name, child_docname, item_row) + child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item" + return set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, item_row) def validate_quantity(child_item, d): if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): From 5164223b99b2792211a4edc9568cb13f2a0d76c2 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 9 Feb 2021 16:22:28 +0530 Subject: [PATCH 644/728] fix: error on group by filter (#24587) Co-authored-by: Afshan <33727827+AfshanKhan@users.noreply.github.com> --- .../monthly_attendance_sheet.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index 46082129e2..c5929c6bf9 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -36,6 +36,8 @@ def execute(filters=None): conditions, filters = get_conditions(filters) columns, days = get_columns(filters) att_map = get_attendance_list(conditions, filters) + if not att_map: + return columns, [], None, None if filters.group_by: emp_map, group_by_parameters = get_employee_details(filters.group_by, filters.company) @@ -65,10 +67,14 @@ def execute(filters=None): if filters.group_by: emp_att_map = {} for parameter in group_by_parameters: - data.append([ ""+ parameter + ""]) - record, aaa = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=leave_list) - emp_att_map.update(aaa) - data += record + emp_map_set = set([key for key in emp_map[parameter].keys()]) + att_map_set = set([key for key in att_map.keys()]) + if (att_map_set & emp_map_set): + parameter_row = [""+ parameter + ""] + ['' for day in range(filters["total_days_in_month"] + 2)] + data.append(parameter_row) + record, emp_att_data = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=leave_list) + emp_att_map.update(emp_att_data) + data += record else: record, emp_att_map = add_data(emp_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=leave_list) data += record @@ -237,6 +243,9 @@ def get_attendance_list(conditions, filters): status from tabAttendance where docstatus = 1 %s order by employee, attendance_date""" % conditions, filters, as_dict=1) + if not attendance_list: + msgprint(_("No attendance record found"), alert=True, indicator="orange") + att_map = {} for d in attendance_list: att_map.setdefault(d.employee, frappe._dict()).setdefault(d.day_of_month, "") From 1ab95413beacf98f7f7627ee2f8b6a3a2b0930ce Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 9 Feb 2021 17:35:17 +0530 Subject: [PATCH 645/728] fix: conditions simplified --- .../lower_deduction_certificate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py index b0a656af30..ad60db0559 100644 --- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py +++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py @@ -38,9 +38,9 @@ class LowerDeductionCertificate(Document): def are_dates_overlapping(self,duplicate_certificate): valid_from = duplicate_certificate.valid_from valid_upto = duplicate_certificate.valid_upto - if valid_from <= getdate(self.valid_from) and getdate(self.valid_from) <= valid_upto: + if valid_from <= getdate(self.valid_from) <= valid_upto: return True - elif valid_from <= getdate(self.valid_upto) and getdate(self.valid_upto) <= valid_upto: + elif valid_from <= getdate(self.valid_upto) <= valid_upto: return True elif getdate(self.valid_from) <= valid_from and valid_upto <= getdate(self.valid_upto): return True From 6fa44fefc3907faeb7807a523bb83bae72ff9977 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 10 Feb 2021 15:48:32 +0530 Subject: [PATCH 646/728] fix: Use new favicon --- erpnext/hooks.py | 2 +- erpnext/public/images/erpnext-favicon.svg | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 erpnext/public/images/erpnext-favicon.svg diff --git a/erpnext/hooks.py b/erpnext/hooks.py index bc1d7628ff..109d9216e7 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -78,7 +78,7 @@ website_generators = ["Item Group", "Item", "BOM", "Sales Partner", "Job Opening", "Student Admission"] website_context = { - "favicon": "/assets/erpnext/images/favicon.png", + "favicon": "/assets/erpnext/images/erpnext-favicon.svg", "splash_image": "/assets/erpnext/images/erpnext-logo.svg" } diff --git a/erpnext/public/images/erpnext-favicon.svg b/erpnext/public/images/erpnext-favicon.svg new file mode 100644 index 0000000000..a3ac3bb2ce --- /dev/null +++ b/erpnext/public/images/erpnext-favicon.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From 5f27c9e100cd2acf6c95380fb2c634acd069bad2 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 10 Feb 2021 15:48:58 +0530 Subject: [PATCH 647/728] chore: Remove unnecessary image files --- erpnext/public/images/erp-icon.svg | 21 ------------------ erpnext/public/images/erpnext-12.svg | 26 ----------------------- erpnext/public/images/erpnext-footer.png | Bin 1937 -> 0 bytes erpnext/public/images/erpnext-logo.png | Bin 11639 -> 0 bytes erpnext/public/images/erpnext_logo.svg | 4 ---- erpnext/public/images/favicon.png | Bin 1696 -> 0 bytes erpnext/public/images/splash.png | Bin 10615 -> 0 bytes 7 files changed, 51 deletions(-) delete mode 100644 erpnext/public/images/erp-icon.svg delete mode 100644 erpnext/public/images/erpnext-12.svg delete mode 100644 erpnext/public/images/erpnext-footer.png delete mode 100644 erpnext/public/images/erpnext-logo.png delete mode 100644 erpnext/public/images/erpnext_logo.svg delete mode 100644 erpnext/public/images/favicon.png delete mode 100644 erpnext/public/images/splash.png diff --git a/erpnext/public/images/erp-icon.svg b/erpnext/public/images/erp-icon.svg deleted file mode 100644 index 6bec40cc62..0000000000 --- a/erpnext/public/images/erp-icon.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - erpnext-logo - Created with Sketch. - - - - - \ No newline at end of file diff --git a/erpnext/public/images/erpnext-12.svg b/erpnext/public/images/erpnext-12.svg deleted file mode 100644 index fcc8e46fdd..0000000000 --- a/erpnext/public/images/erpnext-12.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - version-12 - Created with Sketch. - - - - - - - - - - - - - - - - - 12 - - - - \ No newline at end of file diff --git a/erpnext/public/images/erpnext-footer.png b/erpnext/public/images/erpnext-footer.png deleted file mode 100644 index ffff7756b7794c22794636846f8feacddd31cb7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1937 zcmZ`)Yfw{16pmJlV)a(rop<-=IJ3bhTRa%P$TSpN!0umsBkOvTwKp+8>?j|U=8Ql5qW6z#%zjMys z-McR>B_Sv}9h1tlf&)4_8CxWeYW2JYHIVGMZ8Ql}@xlB(~8SwLsLGBHa{A}t7p z*DTWMZ$n6=Iqyg_gt$pfrtK3A{-pu7(3%?#Y2-Leg_2pco=cJ=*OqLHzkg1U3JSNwhkiU7lB0g3eCReIZwHnZP ztSJi7=I<|@mf@{@p+PkI6H--Dkf{j9#bZnf5N-bcSu+U`jRXQLJts}7L1jX|6cp5( z1)I)AW*qP*#$hr{wN5D#qcAWpg<$gc_R3^U%P?0FWe5WVqD0J5=jUug#W-QLz?yl# z{aDdfgka_(GkSBum%tHV5@~J`UxcGPCY`~e(pgj{GlRk8&{>>SY%b#GLx=#5#Hmmb zj;S&*OvdwOA%|UZBrTE#7SP{gip)jeJ|gTZ-=_6i!LH>60_KYofERdQS92R2DD%>i zGve>H7#_6fh-hxnHMe#=Z0mdk@D!lyF+h8l7DBJp>7PIto^%*mIt;Cy-ECbxPXY8j z?RsOo!KgFz>biS7AWWS-<}RaIZ?x!pEd~gy!DQ_QF!%MC`;3b<e?*f_w%IKTvG&nBMv+H=?@VJqJrbpafdbMf?)b)i>lM%{g#*Y1KM z>67>|8Ho)8+GnY4AA|(h$DGOy8y9MCZBhIfoV8%CMR{y~iimloX@B;=qmz@V1?&H~ zQU6AEb41oB#n(2Sxn68@wx44sZFpMtR*qP3R2zOycw)uFik0uKrJk`&9<6Dkgq^zd zbHR1A*A(9HNMkCyQ~$MYS6xMZ{lX*mpsVze^Cz>oEZ6L=lX(YgPIlj0th{t$<1#bF zdZ1%|foJXWzw>!~>GP(}%}uw5u1nXKR~8>1 z)T9*^9&jA`t)w6$Q+Kl}pz*#X^l+Z)t4ZxQ5BL0v34@P>|C#w-*DwPbNT$D6a1LXKSea25``EgPl|U}-`pgx zk2Wp;ZZA9Kvc_Di2w1W&^rKBx4G*HX-B`bWZ}Qoc1+@|BCC7Imi@rR#b7{`^7iNc~ zR#~EIqpRi!#^)8qO!P?#z8G#C*zc(7V-NBwmNXeP^3wV`V^!X5HR#4q6pILBGN1f(oyLG zK`AOIN*5^+5D207638AB-hOv~*neO@@2BM6J7>5Ua$*Y1q zAmhH@sxbYqn3R<=`{~bZ#G1t0@jHcH&xq%IzOkQiYJYiPlXNk`EWCGzhL3wz(;mam zs0e+J`ZsNdWnaL%1s@oR-{|@nY0_G}Z*~;Fq-{^TrrOsRdZj;jv2vtvX*DTpP}?JQ z-LAUX-M+t~S!d1WM@>Vyf2HVnw{UjwT8po7l6a-v!xZ_9(-tuYwC9?`o1K;7s3_7s zqyP$yGqrmwBco62Ww$mzZntUOgC;^Scr1HM&#Zo;gOssu_1irIaaJx;co%=` zqPH`A^4O0Z?8INVDTT_Zlu~bklA!Km z4k9g&mRv@_Ii{M=RLdq0xEy;OuxhZ$5#o~2Nom1xQ`a1vL%CK*zxRoSX-`_L$BI5X zk=8l?t#_AC=vZ4dMu<@U?L+wH0^~af6g%m)pUB!=qijchzg@5H{&q8c$gZLoA7)}K zZGL~_sT{kZ?xygG;g7G ziU8`n4oNxVjf#U?Hc!BB)`98Q?<#C*j?tNXNMER$}tG_){YX{v6` zXnmpPuCrUoCCtMW1t&FR(9qS^`m0_#-{R!XikKb zX(tE$-0Caxt8*QHwZ%gn&b@2cM0)i@Zsiqz-yIWAH>oS%;~kWuIVYjf^gQA!2g>CS zVWNB4u6(YBGva69njBL!P9k=v>r(qmck8Ic;J6M2LCZt(!Huf8IExIP^}e5f^iJ*C z)Q^Bz6k0gdspvpn>Fk#SP6tZ^;thDAU8;*VSAL1OoV?Gt7~N?QEW<>3ljqD#k(EVk zNv^$>m&>6j10IOVzx+O+zi-)OtcyLmU43mg6A+OiTI%|9c6ojN*3GaL`aSmdb`PSV zIoyUEgFWUthRG|r2QE5PcC5L~H(z6ECl4RAa)d!`-3XgYb@;WDDL3pn0HLnhT*>nb zb7+1U2nIK%4%^8g^tc*Zd$yc@QdE4#G3p-+S^S4LH-l4Qq3=sHc7 z$^4*bfG72h8t%I;N@cnPnsr$)ElQ*}=u(1MvUx+Ma*hm>R5-YyPRHJgFRAmApE|#= zI39%uUis!bkK#;RGJ-l?dNnnBZ!Gt{^G#jjm+^FBlBv9tRC)Qj4q+YB9zLhu^Zpw& zG_`XwcR;C?53@0Ms)&!J)k^ZLmTmLW1~kVFd34sVwR{azlnPadJIP|B$pQ7Xz14S7 z>sZ2^EYY79q}09PsC94A|BKe5tMHaQ-oq{Ow07I%JJ_y|x%DQ@Z6n7`_?+k_eP+1b z?qe}~U)K9eP1#zew#Jw^(eGpGT(H$l@4H(s^6+i40r#0FNkFU!t<;Me#kcTYpRHR9 zl{V64`yS1ok!!XQN<1EC3I*PIzF$j9;eM{q?AeZMnsk~n znX$}6Ti2agMKS!TXNl4s!mAe3RzAs}tIg9fO!VD*m9-yds8_n=$E&dwT)|AelN8j& z>{6ipXNzD&i#;*4!RYY`0 z**w$C$&3*guHQ~h`_D+ScqQWyvl6Kzt0zoT_@VwaXU7LsYlQA+s&tkp5gL2$3@=m5 zHn7HE`RmuEFHc-NaFeBpOs=LRS}H$8jey{2UK5sn{EV@~j=GZ26%foES5qiAoerE< z*xKqhJTR}>H_TZ>5-RqJif*L|-^zc*3I#|%Gi+UrIs*-Lxzwdg9<)^7QiU|>b-473 zbSVd!%Fxjl5oDgzNv@aqY|am|H3Xn6wYR~0vm#j(sU4?Kl3a2fI)iH#M+T^}HU%vE z6c~L;{vtH+a&N`8x-oN`jD1^z1QK3*{PHPWZN2|-!*S-z?--KS(ufC?1REV*&brOS z+ay;IWL(Z*Wc}TblpgS(RW1XJN37<1QTFX2?fpL3m-7C`JDy0=cfOs5%X!42MMh^9oox08_6O9kcRgA+pH70&W<2%C_dWG6uUWqOO|r3 zV<}++4o4G2Oi{}*jx?K85i9JASNav}r1iVJ3>O8E>#Q3H?Ha;-dZmL`N` zo3^g-agF~JD)b$}rDzr#c?QZ16iOS>=Q9LXLiyqM_n{!Ofy^B}8CK>|YP6ilBQCpU z9IAy2{Vvytnc!}ceHsjgMw%Laj!3v+Fb1BObCjwzS>VO^Zrpf63&dcP;O&Xo!IeJO z$c0>glkQu@m5#>>RC}DKWC3>){iL^~fQwR-b?Tqh)e$Y!b~-}Q!%D!-i{no89v)1Z7pFcX zoS$8?c;-kt(yTSjAcYiWbCP_JNNCVfiJpK9I8j^CCs`O0_*>wMk~8I9t4!aVRA=Q5 z2LCxT2u@C*m}-yHTnLo&)u(B;s-$wVc(!cc{P5~&FZf4r>LYODNKSU=6%)p@xp)#< zmn0G6GJ7v<%FYlQ0Ed&OJtqPq&{aOo6|Jw$ZC9JoGxx}PBzk)%Ayk|X{z6y)GNd_; z|LEITj1;XZ=$aKa$C{JgHpi(gw5i*^g_8_~1R#;vmbgnopE5R@gL*xu&YBa^BA;Ag zvn3a_kTE7`NbI3sO+(qxz3b7o8YCFPrt%W;VXg_51a^nRyu$oQkE>J$QZs_rf(1~@ zCFm-3o2mnD91Xek-k~bP;7-C_^cQ(Hukws{2taZ^XXc|Ik_>y; zbhu2T&3poo!r>atL%=s*;?|nCtR0MRb{sfL`VE-+xKM-#4!mjqcbY-S#MH3kS8jxe zH5#%zW3CB;2$=%!XE@hBQV7Sn^1yinfw4SybpQ(vSyI3$580UG>52Nvr2^EsGwtUM z5UZkhPdB<4hOD*v2bdBF-yRi>{6jB1I5jTp7<%osaDNCJs6Cv%MS3x zSfctTlw78lVsZLI(F~v=Q=sd|?+3S|EFX|lv<3SaVYNCC1eU;RTb=~NI>6*ykso1Wf?DeCs(=W7Fq9^TohF5lkr3~RG z&F%*WO_~YO<0J(gQq`u7LW{4&CL|t+GpJDj44(bVvRplcRK2XP&j*tNac`dyAofK_Nguy>ZG$mKj4cxscFN0SpW3I@t5WzHH`29oYs@?~IgVLhaK- zL;KD>XlJGJ?f^BfJ}Z@bfI>kJl)t#M2t5i=b38X@CJZJ(StkFo^RIUPHMakCb{gHi zFz`BVdPRxRc(0Y`O0GhP#m=`*6|KIVH6!05y?)6^5wy-be&{)=ICRsx_96{WHyHg3SSO)NVyG2_pIM41H$YUW%;{jkCbc zgV&>9CGSEcE}>7I@Fd@FH~dZT$Zht(HUCx6D}?&aM{4X7yIZX>s+YC{xR?ibZRBBa zBjHa=+tg_OMox845gL2JWyM`h_ym!B9AT4McqiA(G|dS(s)~A39G8IDb8=SaHlLAe z80-h&*SK~G^zKGuL*PnbKDD3a~r=w^-IqyGJFvtz_i;ix>tNhmXDCfWg_h#=A~g7Mdu4TiL## zWnl{<+6mjRxVqg7-U}jbxq4m}Son;11mgYe*`WbM;eX=QM+9=46{Tz>q71(#r>qXR z7>c=>0+OUlq|h~BOd(Cl-T`n~knR{FCX^9Kvnuk1c!MDUieB1(Lc!d25ZMEBFYSdV z0}W~%j=2p=(}xjKL2ic^&r2h^ccY+p7n0`hL8RTAs*9432ob1|SJnf&A$TM5z;03` zXQcrTf@;bI%>yJK5LO27;T=I0#gIbclqvRnk}x@nB;5s(O!nM00r1`$Yr5Lq&sa^p z->tbWqpSt2E}(ePj7Yx0qqXi=o1r6I=-PY(e`X1}O%MB1VDa)}5WW%uNFg=-E{vr} z*OVvAd1ENjGl22Lxgx7gj3}3Bd}p$t%f{8(jrIH8rT}dSK)YNY2T}~cIL1J0_GGPU zwcyO?3!(43oq&kI=O#AFG!UUJrL#J&l(7*x6{0!tIwKiOdoi7xIF7PMKQMZlI||3=h*6NG=$H*muK%|`z(S5T01L#F*CRtJ92 zMqk3WDWu~aA4!(|)hn6B3C*izdx6hGLkVqnf8^igajd*Fbjyz%$2HzBT^Nk7&2yrb z9T|AyDl5gXuWjbjNewH%zIWw?5y&0;ray=pI$pD{-PI>B^HJI`WZ|HoE+dd6V3L9a zUQ{MRPaa%ijIqY$^wpfVGB){f%O>0~e6RQq+QU6*9mWFF3PfWu_E6ybPLeKCZ3|SZ z)d%7k7iwq!7;N7c6aylO*osS zUXz0h4ktecg~n)e_gf$;xXF3{HW18LfAO=pSVnm>7H1|bfNPMlK1sF8CsMP-1AYWV zeQ_~oaBIT$!1IIJe=qTg;RzDe6+j&L1{+Y+e|Ls*z5nv%2LB4{O%X+yx^jU#8Kpgc zF_yT!KCtH~H#8^(YXDt9GzQggzI;(BTcK@F!6!hsOnA2>xF?C6proe1g`_~5g@U$O zl~6(}vM<+!=g%7ms!`da-|W)dt(*;rI~c1fB6EkaBMTG?zOu);{0wn$*q#s%zDVz- zZ96pGvq(}cguwvKv;O}R+mryrEft!s6hVXv#BMM8sK8(pH;q0Iu);T1wP-gCZj7a# z-iC-c2)6Gm>8ixxInufcZPj4#2@uq~51Z%G{Ros<}CH{$IedIVu^3F?? z_i(||8eeD*cOPT8HA#XbLqVkLxART~smg19PLNxJTG5-w7FvS8e?hqt8p$aTDMT`s zlXPyg9?(`aRXQStL?BfJ1I8OL#cy~?D?p)hC2uaVk#M0$KD0OSQan$;BZFw`P`m?^ zwk)`rI-nI-1XjYJr^g;1O1lOR=b;Z3My6C}9#u=rBnA0b za$k{>FeXB!Ymz_G~9@fP>)ZVucIs5(%SJMepKYiraehAp@ z>Gpg>`Y1ybF!Z5AXeaRSIxt&H3N#`EYeG*D@F$$T%nF?Djsj+7rwXNu_RpTTV%9?BJ zA81k4cl6e)ZAsThKG=Q7Vu|s!Umw&nFSccW`3!0VayYZw6F;JzcgzG=i~4lXWX#o` z*>T*&+hIXov^EvJ+)*MkE8RGYQZ@%iK4~>hJDYsaS%lp5lJG&kB;il49J6z!gECp}-@fLcv46u? zJVI6u=B=zgk_6r>G((r6K2CS21`BuC9g9kCkq7Ookis)z8?%oW@b5M&9cfWS98BfZ zTV7_N!mSJ#>nyA-lv&XDt`90#&oAv_JzNEm<_}xu8mkHt1pxKG9%DWH0VlqZ({hPb zIuQbZQHfrxHbo;aoJag=ldR^i8~_wL`fIa{2az5DgH@15_p$~9Fu=pexfiqA(F&lX zdRB*6Zow>tBm&dLw0W}nxn~tYDjfp(rvgv6Y5oFGV&=~X=EFNjK<^4Pro-Yz1_k}g z*#8@iVU4ZpQsj1Zvs5kMmI3;TbK*qTUq3@DSq)gkPHwG(kjjI%tVW(&Au!Q_npS_7 z`JO?s6I0@$#Ic{OUfsLXpp~ctcl+~+s=~AvyE|xZ$eJFcS5a`7`2zJ_k)*OT0u_`w zRd#GvQ-RrH_28E$T~31mBcAT4Sf;&0Bpa@wGJ_a8$L{%gjGv;TJQbd`|*B;1)*D;jZD1EeUk*^LG1|$_HgsX-%3Nj99o&DOuQ z#oP){Q^IBk+Vo&2tjXp3RuME5eq>~IgpC|1`Z6BM>guk#I-kHIEWrx(b*9yKy+#-; zdhFsnVd27C>iP)zmW)mroO2{(4uWR)d#t_E74`XuRHkr=Xgp%fH-sB;OSi>~ zG-ZM3(ioXeKfCqKfxYE#2D*HGs_e)bp-Y@dH$3~EWqNG+Lt`FJ(&Az9$UfSGjfzN0wCpwB1uWmJT+ zp`CH|!h{aj(CDopmPg1#@f+3aBa_WLp$=Tmq(uf_RolIt{#%0<3A^u%Olv)Ew)5+1 zv}YsFpS2Fzvu@@pptZGJgloQKX>|@vSqx^vupcAF`W~P*+&gkB^;lXpOG2kq%t?g3 zi<3MG4e`dih39mUCNYa@oYcRd{$TQS=RGy+2`fprV2*8Hy?=6}uC-r|P~Orv#2- zr!WT|FPZd|bF7vOqtx%p&S(*iASA>M1QAJ@xh#Vhal+zK?$7mTjizvdNhOFn41e0< z%IU$W%}i}K<4>K?CtshG`%lLUPeGqJyS=RzX7*as1Mi3b<74a+&?(__jy--#Y%U13 zIrPc~)R`0O&}u-MN+|2`*rFv8ZXI$gw4c#CxBkVYIz>f??GFACKYh%4W~wzAUfqEk zdBkkygQm-&JHtKJ*#oz4nr)OB@c;>N+B(}$XfLz{I0OY+Tw!)!fKi(xu5xzdye?L9kg85~=7#Y|3T_QjZNQh9(^%{MQl8Z7d9X}r_rdzUFJ8X}c&IoI zM;n60*7$f3@Nx_f+uI0`Mgv<1%$7Iyd#waj z)TvGAzLk%bf@Z(Xm9XVG?UH^vR>yfss_7q&H{dJTVX9Xm`4#4`SBDu%m_TZNX+^e6 zItd$6za!e5!`z;19cXeAJh&B6d2M(9Tq+HDf)`q$cyul*j$`UZS{hqN#$x_Cq5+5c z`%0*i8^g4cef@B??wYGG)5mpzD@wwWUfNTvVTcy?S;r?MP3J5&eg=+$`3}C}qqa+V ziMLA+E6m*`n~CK!^BF%!yE6D&O zOm)4D!8041W*YYGt}7{_9= z=_6S_qn*$^4Xx-mSU+ZU&?2&(Iv2-C>M(W@ncmE9U~mKE{A|zT5wezO+=bcZFBjC= zET#MAF{C*w#IkpJ;PUKO zl5}O|M{k90tqy>)?WU@>kyNM_=`L5R3vzDjI#d^y6AmwyiLE7MLf)Baeuk8yC|dar zMBm-(L#7SDmy!o%6(fC(9h`y&O zBNAsPS=%I>A*mGV+({{su&s0L^vWqV_tup!^0>8hkPzzaa#Rx0_i%gr>4Vg|X zVhS-C z^^%`_6KH$L%wR!CrmE6aO0<%O6ELR1GYkJr?fUAiNzB#f@m8jMpJ(4kz_qJx%GKwTKWOef}0LL{kd6(F)kV6RF~iCSg5W* n`1|v{faQNCip%Tg7_fs)7Fl=4t)GEOYRK5YTtDwP_WFMTgYc}M diff --git a/erpnext/public/images/erpnext_logo.svg b/erpnext/public/images/erpnext_logo.svg deleted file mode 100644 index af3a84953b..0000000000 --- a/erpnext/public/images/erpnext_logo.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/erpnext/public/images/favicon.png b/erpnext/public/images/favicon.png deleted file mode 100644 index b6948856f88b93a5de936e256b09845680fa0ff7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1696 zcmV;R24DG!P)UyL177{GtucjnIOR#~m3>cKWhbRTGfw&DRHk)WF3#h>=APt=1| z5(!4a1Bnn+<6lA$5%HiZBpxK{UwP10O8kqyXoW`XwtLsTGjooI*_OS#YpcC;?zs2- zUN$>(?|k3>?%8kVoIB@?Fb7HEv_8fPC#*rZ7+4IP4)o9U-Y%yO{4B7~IX)4`Tm2{S zd6J0zw>QabJCeld_&B#rOk-Pv{}jy(eR^)KMn*~HH%d}PE8tMD3dZm9`&FFQoJ z>5)g|o7NrLy5a5-ccls+6J#F?%bjKWU3a)^TX#%p;Tt!)t8i=qjJMRfo7_59Kl-S= zduYR9;UgpN92GtV20D^j_i{iD7d`y2?1ssbk|a)5c%2}-3>Mg&SCYgjObQ<#XC0wt zm&x+_0d5A6Byl=^fFHx9U+Mfb|9H;Qg9lj|WP4$0z0L&+tm$+ny@JbGqY4*wI+311 zxL92=E)wv=5~Uc73ix4(0`ohaMGrB5+E_}UT!L`V1mT_u!aWm&dnO3?Oc3swAlx&N z?|^X(S6l(-Er)>tkXi2=CxDVx`}hUWG3k_8INS7+&T#o>$@XD>zJc+&sVWdj2Y9 z|9-#mdRn-1@Y$z+V|u?VtP0k`mMySnkKeeJ5?-&vSG&rOGdc!4cffDIl^?5>N(ukt z4|wyY zU%-};oWK5Beymn1CA_~MR<80B>+;KC>sI(^o1b`x z=-HB@3aeMc(xsl``VChtM2eYpKIv)U04ERnjaRC0YaMB7`LmHzuV;*ox-!EZ6=P$T zg9lUU6FJSMCC|&vv8^Y~EOX?UCC!VUt|j+fJB8ulirlX-$*!G_EOU_MG$*%-fH6uG z2~i{%qivg_eoVr%%#mf5G_%;;O%vS8hacu#xI#JeOwSYSXW@q*9NV_#)3!SYS!Sn9 z36CPdL`p3xK8T}5Hr_dxrWq1REotVk_P_u8#W7rSjp4!-%EE=xk;M&u|J|{DyXBp& z`B5!yAOcaOL^Y)r393i_N6abQ0cmP!HVe^#=|quGiw$w4$Q)vtem8^EvBrU}lQiZxMl7tn|eOWBxWD9ePn?vNZ?V zgLBYqSegxsF+$vDh-*qTHkKaM$uDr^8ES&#eI5z=Q8U~j*IoaC!UDjI`sRre&&W-(|a0C`=E2PKY~3l zHzK%Nqz`S{T)TJ5_L*M1VMBIx+O%)vx%0gCaCcx->SFYU&6}flTldX4c=hCy(L0Mz ztDO+X=9zHGc;u)`7WbLWiFf9fgo63D-PPx%)k3B(TtOO(j~0000LFyEC}M;4tW)d+&Sq z{rA>efBn_f)zzm?b#?7Jb!xBE9jdAR_AiVnW1`%E=u*FnB zK)~Ph(){kKZVaS$bOu;h+nH0ldODg@n|oMWARu@wm8DoGE;q(XznBuWAu9$L^CQqY zDnr-V+5xK00w?lGt6w>GYRzI11KxsPxmLbBLa|=Dqw``V5=55l*e*0I(>>3Pd!V8> zzJ3%6RU&?(qD<}M`G30h2&gi-pg(Uf1Qw1S>^!Mn+0t*LJ6k-k%g6^W zs_~C6oVisRp_zVtw-9f)72As0vZXf?-fV|%;b-=5*;gcsg$!+51=_pzo?96!@U(=A zr6ZXf446jao352UTg$Snn#p;JhVJjAumRUxM4E*9r4KxJHB994?^#w?hFNu_2Y1X^ z@@G^p`t_zA;s~B41!1bxPT9oG=>XPTN|Fp(uI9X{d~QAu6-h{w`9yr|Pw^zy zQMX9-iQ?bWS#g=IG&?`(C9*wf9WK{(U-pR>z7>ccFP2dje2U;6pDL9q3FA2ta6POx zi&w9-F&S;fO08$Fb003#a%g#q5`^9l=y)}pj90wK%uNZ}!Up3F=S8dgCf9tjEJL>W z=rXvu5^ZU>R=LH*EyuOVi|3A$FVlLH56}k{)rELdHqnb?x@?$-m8hB?GMYrkbgAds zhjpLhMo?5%{b*eHzr&aThGt3>Xyb8JS|nbIrr8gT7CW6(fltq80LY77mP?%*jdUF| z-fHQeo=Bb1s6*W1tCVY2Ye)LiFCd^sbHd#stza4btZ6dfRJ?A9)rsj{&2MX(l+Z9&M7(sK0omSRJb7H!&QO9?|L^Kw@8I>|iQYDoaW_$KnMnf?IhB~X??hDeY z+NMK5ZndA;#W}(i31+z^_QXzYXtt-Hc@?Pfh)&`NpKJ9pHgQBXZ+^!x2u$;dikWT^>p$#=s~&T zL&3Oe46bUUKDbrTo@It))M*JpzD!<77>!;z3s3o*m!i(}lynnK?Hs+IuswTKaTYer zr>o7>wt~kI4e%ESAG6ula9Q%%0(SN*eqBhS>q zdWn(FM9Y)%d^KVLw$=*nEthJAy{+~>whm^C7!^V|;sIi}trfGpU8B9mCvAlHj4#Nx zR05t9Pp)ScG!wP#CV4BTks#^G>n+Z??tb1sM3u@76Xaob*_%g^?$!R3Q@@SG_}zYp z#4#8Rld3byhrEuuM8+ItX$f-L#PsjpBO^d> z4gKX7mX3ARe7bqYM55l zjN#vpF2ELp(d&swJrameR%kDt`YWiFKd4W=m}P#<@$?68lwp9!euO`AJL-Fnfk`CH zaMmzIeIo3MtV#DgV1b#vNJtHJ*9GDqw8@Oi&BwIUGrUd)Gpo5uX6GSsJLTVauu8l| z?^UHLRUOm-T_S_l+2}w@X}lN#aWyj!qS6nwwoLWqvL?Ks@L~}1^ zL7SwWdv1vQO-!7^*Z2SvK>~`^G}tN|N)l+`Ta%Ab?&P$;+Ny0!%&$TG&|D@{gXk;% zQpGot9Qt+=2`5KGP7+%aH`+UeiC;)Q~PVn`2L`JY6Z!n-+Wt}(B|bWTi>mRND8Vw zejw$fFqUOmiK(q|UTDpUc`OOq3;2#F|Ua*4w{N?5X&JEv@t|E0$nd&AK-`ybUtW4m|^nwB&#JJ+5!Z7CET&a8W zcaLmyB1m_vwQqA(W6_B>44or^97W_H-8&5 zeP*AU(FTB%oU9XT(XBGXf>h8?K4`F-5I~jcLAsUMQ0yJ)*&xQlOwpFh9X|G7V=p({l|UQbHHq^vpEBLNZ9=L4HsuaHBlBUV8|cSq%AeQQh|&&P(j|HU`yv|nWNLdi*ybf;H@;-Xl?h@W&OSBu zOXE?nXcu{$g}CgU{9%Y{?>K_Aj*|*6zrFIZ4RJ6qM^Yo8M;ri>AtcMm?23^f(9X>M zqW(LnOy?%C9!1!aoyN(TF5#Gr;eM9zCHgDxy0`U0sEP4!^r{;X@l*a{wV7cPfx7j? ztbE1L|~QO zN=!zK{J1DtT@>{{`MVh(^!Tfx6rnjVqX5M!x@qeenR2(}3WpK|e6zKSHBNGRTX}Qg z$c+!nhkyD{WC{Yq+xq<;OA+gI#*&iG4Fno}2 z{g#QnBX1~&l`h6}o2c9TC{Et7v@+g6_lOJ#9&RtuGm&mocI)1TvY7DY#~EQN_xpI~ zN*rHDg}qGXo!Q6RC*h;~$Wx^g+lzf|EV``Mw$oZYhzXuQ+}za921~s}*ZXw0LwKK^ zsF9{VOqdqQIsUv^wjSNBif zX>LSUhSHNu?504k?8ps5w#}iaome-jv@mg#*2OOslMfc>R8PYGhD;qxNQf-u)qp`} z-TRU=lolfRRt< zb3`*vT4-7j;con&E}FQLVzP$UE(7971pZ4=xo)Xo&yMd6mBTc_sn!YY31S4?e$Wxp8JCYz2e1en}(!WB3X#NBqiQEZf5l@E{GroDXkEknl zG4#_-xdR27`J~3;v;Kgir^0w>wQm2#AlndxQ>MB3qfKh>BvzMNXuJuQT)B2ZVkaZX zaAl#$niT6$&l6^y`zbsNlbZYuxevJnIV!$whuLHKlb^553sLQz-A}F{6m%}4E^=xb zR`*9VXaDdCJL?z}&8Nu12jE|*f(OIgX)m{o(6Q?s$a#6*}ul$*835S|y~ zuw?98e6{IcK>P0|9~vcUM>Df5(ty<@_g9uJNRSq7EMrkNayw#4N&W%prW$;870r$9 zVm>feO38PU&W=ctyOWmR(HMhw6_LXq7Op$dYgF-4RXSef?Y1B z@UCM_2luJzpvfSxRC$Gcdc7Osf;wWvOy8<@Tf3lNZcOqlS=V*&Cja0fM&P{cI9;`u zWmEd>B=x>KY-*A@{|nQDDXY#x=63qU)Qegr@%Pyb1MjsW9e`c5bUIi@@Ds^`5RV&h zJ9?D&169(tFa4>6Yn#SO&4lN>l#v9+kwuopuiQGG`?eZ^`K#M2e)~V=ap@-l~zbw%S4B6pTPZllH{*G?ZJQW z!}g;uFhZ*@zEfN)nTlkkTki*k$f}T@HF^n=trcUDEwmkDzz~JrRBT8sjlSg!H^L*% zgU#`4C+Z~z$%|oAC<3RVuM@AQof==CaCLurvjd&4d@;LgP z`$FjTaZD7nm`bL%sCz|M4pphJe-vY4X=qnp z6!dDU)o){h_ShgDKmVp2&Pun-P4Q&K4o z9)B~Yc+vK!jimIwzB}gMk*F%%IagabC&doY+OP2MMFJfQlRhS^zV=8Z_nt1o%bZhM zrSPZ@BoM79qa_#fw}6!QlzMzsJEJy9R--yQ>zCT0rptpgQf1+9wk4YrpkY#K8LOg;Q6g5qC0E5Hv8nMc1Q_>!+ z0!u8GO;pI1)Q}oGV-kZEJs?Yu`msIpzO(Rq2J{*0Zf0s_Q5@ z(uXmf{b{PFFJl)&^r!Bz&d^fTH=Se2TFQhdTd;xm=MzeYK*+`if&~<8Hpkoq=IGyk z?(J44IhE?E=2!t*T&vVkofKh(t*G{yNa(RvG#I#~2JQ$S?_aNpTU8P7sncO#38S-7 znpTNr08&~{@iOgm^bs|#+a&#J=ZpjrcgGlRYDS4{J`*9Jz0OoTBWzwaM>x!`e9icg z1N~GM$Pp|4_S^8&mZP$U-*3KpZ!$7WKCcTAnwn?KCKz!dH>q3GZ#yx<+P2mx3rqd{ zrjWpLJyQIJPD^BRl;)5gE;pkryKunLg8?}aatm%v%Ah=2-WMY3P=`YQ9}y|iECB<# z3}gc$sP9#~-@iNcU*K3FT{E_Li*o*QW9Ig{apuaSQ1~k>&+5V9!^hzFk4`?BvIm&9gARu56{NsFukdpQmE<|;eQ<6g6L_$TuMSiUy zy#*J&Hc^m~M0okfk=0fhhk!s#EGH?h>9KT@errguxcsZ#@3_TQo8-(ZCe=OC> zgs*lBxENAl@8LPISY8ONlt75$l4s_}7QW;kG(UcGHJy$>b+ZqWGr5#|j=3z2ybyl5 znSE(Qegnn}1pHVhqq&p*XAv3lrjz%}StKrq?mvtFRrznd|Eg3qIX^isDrUYuN%gw@ z_EoDyA!+7d`f$KFAtsv(d;LA1pl-TAepr~MXOq{p{Pk5))yEy@&f72B?j@mU*Y|Y- z4-G>jn#=WnmB^e}y{?ka;B@>`uG#=o!YUf&*| z>0K~ZR;J<{YuVd-4X0&+hB^FR4DwG^Wg^p<5%WC>9}P@_?Y2+-Ic)TShPKk52Oo5MeGqO}q-J-~0<2$u zIM7H0ZXtl(KR?3~w^R8Q$R`#T6*2-j_#qcaV;TAEP6$wid8&9^2xxp{zk%5;6v_P~ zD(Y4mG;{hM#OfCM)aJ7s%fY1Q3@$939iy0Gz;1ObD`XmjJ)cV$a!KoN`k$VL`GMb& z_qa4+fIot|d4B=!`p&Q*Z^0X6-Mk>v|9$5;(|ApGi>b49+AQJLk$JRk3x69 z0I3(NkBry`x~Pydim>JuQy(+9njj&6`n@;9)=oO-8AUj#MNdGwYWKi8{W$0H{EA2a zE{Y#Mr(Z!*JCxCEd)xaJbm1*pogKiD^D^th1+{wRu798kNBMtC|3sGz4*ma<{$q0B z&7#;L{m}=4=Cl@M?O7OL(tc_^w>a=&pzruVX0n6efZq5#hnmT>06Td>%khBFO*c25!CrXJNLFT&C#y)1GkXm% z!;egUEO-gBhx1Ld1Fo0^y_fk%+nttrVmM-85Q$*`;I5t-BI@@yC>dVEv-4n@`fQ;C zA0F9-uy9!vh3PXs_++4=J6qmi$&!W!ZZc5*^iVXnov^ZRgIz$@ixYR58Rf#mldo9U zhaJ8Tzp)@6hZ@Y@kgyk}9lzlh)XmR7*s~ti7djh#R=6>Q!w0v^eC9OgU&4Od~)?K{g!Ee4fG?0?2UtgbJ?R($-g8m%b zQbR&I>_LMMOYq`kcE3m)ENLZLM~#3Go>Ta=PmuA-u3X3x$xE@{A}wuSxds<3x_~MxC#=uA z{Wi*FZriW+PovO}&e{-4=PMo(+*mVFt>Th6rb)@{|Kxz4K9tQNmUmYuYocIpm%m&W{vx`iFZEQPy>nWpcuW5 zOji9Ft$5Ng!>0*?D1h8WE7EKhL3EY^VpG-(gWBC(aEVq^#}Z|hLKN6tNi|(9^|I~b zARUQGHr*T_JFD<3>oA7#T7=aM&`}Ug`^9fg?6ZwOSbI~f71|UY1HBF)z)EGUDTvR# z8%A}%U1V)BBLW)6!GK0!Mrj;vJhmUNV1p_Q_Y$#DUy-HmzG#&>ySaO9rb3IEam==w zRwg(8!}0#p<^Gq8{w>-=3tTHaiq7Tbei1pa<3oX(K7( zgu!^hMU`h$fYyKv`hGw$&#cG-yF^q^M$d4>Bi>$!fkByFtd&P zGT9y3KYPclKOAX|hwW%G@jP7HHozlRJXQx8Z+~sg*v?x?NKgu%!AV6mm%8KeR;~=i z7=wxmZC684$@JyC{0E|!o4EcMk1*>WckWH`D;1g_Zs0pO&*z;g97s+gF>jmVae6%~ zNA!eXG;M#w6&KDPZdX;kPbv@pJDNkdWPg|>`YNS(UGk>de5uCrG^CA*E(bJR@qx{p znjt$qX)mIqQqJ}6xh_q0=XXB+-v)=t`vdDRA3xg@^*ofCq4JugI=Q=5*Bh)~r|l{x>lNxGkiiwvpE% z>)!q}fu**&dB1}~2`=<|K3=Q|Ud5@uhFjH#{DHLPuzpuxlFeR+I)~YNT)$sb903jl!i6t zfH!MRFS1gjxwu0sm8O16*()p4QY00t>=nLzpMZD{TQt%BKME^^UbyJ zk3JXX&?+(@rN`8rP4@iBMP$oSeg5FafwpH#_p9ov0id@f(8`RgGbQD|YJh9rUP06^ zKhMwceZ-z;^;vb#ZWB7_ezOjWme=M9Y4*2}oQ_q$8Kr!_ksurBqlMuAARcV+=tvs= zJxn+tKm51bI|F4e4C(c}A3NlekIUt05S{6kxIqKpDX7o1gi0ioqm?w{v-wWOpped4 z)^lCr8p#(pD4yLbeiKICUP~rCV(vrw}3Y^yj(hEm%s0)bJfnlUIpd zIU+-I(n<-JZ&PEQ9rInrWbN*)ecknVIi#Eq2-~fj3L8rCYTCWT?r6~~k1EfQgE)OS zI$B(v7UUAxy#+=Snly7oa-{A=*F5ucBSDOJb>q{4a~_)BmbbL9(-SgIO6NPtY!par zrzIuZS)|R0@pcPfy=8^Fz}sVPu!S3{IX8y#ZMZ*bv<1hjKWVq^T=FSMVhZ6o=|F$7 z)-O1EsD#R1PWY{2^kc_HT#y9a8HF-<9EQChN7gJ0Mp?XG_A9&|Y~fAe7F7w|~5 zJ9Mh%l7!!Pa}+%y_>YD3-li^<$71T|hnN*Nq=0W&$lbu{BcKR@G1K!qOjqW-ajmbn z3n3N7EXT9yVLGPPekA(bKH{gurV$IVUIXoZ?;IcV+HwWe`c!yM`6{g~f7LN~q_wNl zMp}HoOXQq8JI=3iZxgJ7iWEr0kNKJx+89Tny?T$Td$ga-Xgoa5YDw;Q{HF? z+I`Eo{wueIh%8rzl;Rpp|!T=`333a4^!F&>09v~>ac zS)!1&(_$wNr`v$sK8MWDkk}qF(FA2PPB&vrn6r$W1j(80)StH)93_7R9q+cG;(cw} z_`78<**!Zg&j;bB83wkxrBm3Ey|tySm%R-lZa&x#K8C;~w^udk;?q-E6 zcON#k_QLlbdP^FJ_c+>T{QQ2ou51X}fWncnr>jK{`cMeEx_vXmFW78Ai^nynn|WZ= zt`tC1e+0k8=|K+4eW0rp;@JA5;LMW`pQl#A3!BrHG6P5^4iK}wSWtXmin6z-FGeB- zmin3GaCaO~WDoDs$F}8s8fvUxLNC6wko~C%=0o<67HUt^j2dNnU?<*0SVqRPwO%XD zZK1Vybttqm8*i(S#%`?Px6_y*X>gwwAW|tO57h0VsM`hnaYrooVEd(Y&N&$p(+ym0Dju zKFq0n9B#PKr8jl`x@tO|Q~@dH^xe2_Ni=#!YeJk-y@L{8A09RMZsi1Bm9{Qo=UaDO z7nswsi|Ll?`9nF+*#5D9F5_;uD%3JmL~4VdZpFTz3G;}=#8*S!LW6(Flnp19S{!9a ztU(_tL}C2grh2ZGzTVB}@1*a4*nZ16k7uPEyjbP+X$H2ltKES$v{_(!XAVyb8=|Ac zk2-j0vISDNQ>CaYUvV)&C5GPAa36q+2^j+9sBvxG7jo=+J@*XrLL>R3UutKPFSW|D zKPffplv2g_O*e8GM47iA^C| zlV!@pSg2@=#p-Munl&~5X%~q&kuu~3;pRDwpl`xK@8*PKP%h%guGeK0SC@Bn%48t` zFd1}jNqmVimuSPz&dj|NKc#Z$0Wa(0WtM`bOY z8G^Q3=;@t<&nUkO{)T^SODXd41aXpJEdAZiY_#1}yPg0RCiZCE!Yu6!7FP84h*flk z4WI8BS0s^wgd~4ACab9DgP(X2w_@v+znEHUU*{hIk-?}~NRVI>*S{A} zMj1(5A|GMpJGoa>^crm%Y7cqmv(Zuwk!b(qssGO~>VGof{}zk=7i0c!vDp8M;hM<4 a;FvxakH}_neuKx15#*$lCCk4U1^yqOq<=I3 From fd0e8a4e2320ae65dfb058ad9d72cf55f8ec8304 Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 10 Feb 2021 16:31:41 +0530 Subject: [PATCH 648/728] fix: also fetch item variants if hide variants is disabled --- erpnext/shopping_cart/product_query.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/shopping_cart/product_query.py b/erpnext/shopping_cart/product_query.py index 8daf3d7dac..36d446ed0f 100644 --- a/erpnext/shopping_cart/product_query.py +++ b/erpnext/shopping_cart/product_query.py @@ -23,8 +23,10 @@ class ProductQuery: self.cart_settings = frappe.get_doc("Shopping Cart Settings") self.page_length = self.settings.products_per_page or 20 self.fields = ['name', 'item_name', 'item_code', 'website_image', 'variant_of', 'has_variants', 'item_group', 'image', 'web_long_description', 'description', 'route'] - self.filters = [['show_in_website', '=', 1]] - self.or_filters = [] + self.filters = [] + self.or_filters = [['show_in_website', '=', 1]] + if not self.settings.get('hide_variants'): + self.or_filters.append(['show_variant_in_website', '=', 1]) def query(self, attributes=None, fields=None, search_term=None, start=0): """Summary From 3237f5bb88d5a60e7c0a75289d652c24cf2fa10c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 10 Feb 2021 16:36:49 +0530 Subject: [PATCH 649/728] fix: Inpatient Medication Orders report test (#24596) --- .../doctype/inpatient_record/test_inpatient_record.py | 2 +- .../test_inpatient_medication_orders.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py index ea0d1e982d..a8c7720a0a 100644 --- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py @@ -145,7 +145,7 @@ def create_inpatient(patient): def get_healthcare_service_unit(unit_name=None): if not unit_name: - service_unit = get_random("Healthcare Service Unit", filters={"inpatient_occupancy": 1}) + service_unit = get_random("Healthcare Service Unit", filters={"inpatient_occupancy": 1, "company": "_Test Company"}) else: service_unit = frappe.db.exists("Healthcare Service Unit", {"healthcare_service_unit_name": unit_name}) 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 index 0d3f45f500..4b461f1a97 100644 --- a/erpnext/healthcare/report/inpatient_medication_orders/test_inpatient_medication_orders.py +++ b/erpnext/healthcare/report/inpatient_medication_orders/test_inpatient_medication_orders.py @@ -119,7 +119,7 @@ def create_records(patient): ip_record.expected_length_of_stay = 0 ip_record.save() ip_record.reload() - service_unit = get_healthcare_service_unit() + service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy') admit_patient(ip_record, service_unit, now_datetime()) ipmo = create_ipmo(patient) From 7b2afaf3497615599d74c294722c919dca801a85 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 10 Feb 2021 17:21:12 +0530 Subject: [PATCH 650/728] fix: NoneType has no len() (#24600) --- erpnext/regional/india/e_invoice/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index e64be4b645..5f701f2aa6 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -23,7 +23,7 @@ def validate_einvoice_fields(doc): invalid_doctype = doc.doctype != 'Sales Invoice' invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export'] company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin') - no_taxes_applied = len(doc.get('taxes')) == 0 + no_taxes_applied = len(doc.get('taxes', [])) == 0 if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction or no_taxes_applied: return From df6e20887d85500c312e2a1bf478d41e38e1a771 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 11 Feb 2021 11:04:39 +0530 Subject: [PATCH 651/728] fix: update total in words after updating items (#24602) * fix: Update total in words after Updating items Update total in words after Updating items in sales/purchase orders. Port of #24592 Closes ISS-20-21-09425 * test: Add test for total & words after update item Add test for total & words after updating items in sales order. --- erpnext/controllers/accounts_controller.py | 1 + erpnext/selling/doctype/sales_order/test_sales_order.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 21874fe2ca..991eef1d21 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1521,6 +1521,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.flags.ignore_validate_update_after_submit = True parent.set_qty_as_per_stock_uom() parent.calculate_taxes_and_totals() + parent.set_total_in_words() if parent_doctype == "Sales Order": make_packing_list(parent) parent.set_gross_profit() diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index e259367e58..cbfab8204f 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -325,6 +325,9 @@ class TestSalesOrder(unittest.TestCase): create_dn_against_so(so.name, 4) make_sales_invoice(so.name) + prev_total = so.get("base_total") + prev_total_in_words = so.get("base_in_words") + first_item_of_so = so.get("items")[0] trans_item = json.dumps([ {'item_code' : first_item_of_so.item_code, 'rate' : first_item_of_so.rate, \ @@ -340,6 +343,12 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(so.get("items")[-1].amount, 1400) self.assertEqual(so.status, 'To Deliver and Bill') + updated_total = so.get("base_total") + updated_total_in_words = so.get("base_in_words") + + self.assertEqual(updated_total, prev_total+1400) + self.assertNotEqual(updated_total_in_words, prev_total_in_words) + def test_update_child_removing_item(self): so = make_sales_order(**{ "item_list": [{ From 8609d3143099e4814da44a0c689b899653a710ef Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Thu, 11 Feb 2021 11:05:48 +0530 Subject: [PATCH 652/728] fix: amend cancelled job offer (#24589) --- erpnext/hr/doctype/job_offer/job_offer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py index c397a3f5ca..7e650f7691 100644 --- a/erpnext/hr/doctype/job_offer/job_offer.py +++ b/erpnext/hr/doctype/job_offer/job_offer.py @@ -16,7 +16,7 @@ class JobOffer(Document): def validate(self): self.validate_vacancies() - job_offer = frappe.db.exists("Job Offer",{"job_applicant": self.job_applicant}) + job_offer = frappe.db.exists("Job Offer",{"job_applicant": self.job_applicant, "docstatus": ["!=", 2]}) if job_offer and job_offer != self.name: frappe.throw(_("Job Offer: {0} is already for Job Applicant: {1}").format(frappe.bold(job_offer), frappe.bold(self.job_applicant))) From e3c72a3d718e6d560d8eed0c18609f0723afd6cd Mon Sep 17 00:00:00 2001 From: Anuja Pawar <60467153+Anuja-pawar@users.noreply.github.com> Date: Thu, 11 Feb 2021 11:06:12 +0530 Subject: [PATCH 653/728] fix: fix filters for the report (#24594) --- erpnext/regional/report/irs_1099/irs_1099.py | 22 +++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/erpnext/regional/report/irs_1099/irs_1099.py b/erpnext/regional/report/irs_1099/irs_1099.py index c1c8aedc9f..4e57ff7ea3 100644 --- a/erpnext/regional/report/irs_1099/irs_1099.py +++ b/erpnext/regional/report/irs_1099/irs_1099.py @@ -32,6 +32,10 @@ def execute(filters=None): data = [] columns = get_columns() + conditions = "" + if filters.supplier_group: + conditions += "AND s.supplier_group = %s" %frappe.db.escape(filters.get("supplier_group")) + data = frappe.db.sql(""" SELECT s.supplier_group as "supplier_group", @@ -46,15 +50,17 @@ def execute(filters=None): AND s.irs_1099 = 1 AND gl.fiscal_year = %(fiscal_year)s AND gl.party_type = "Supplier" + AND gl.company = %(company)s + {conditions} + GROUP BY gl.party + ORDER BY - gl.party DESC - """, { - "fiscal_year": filters.fiscal_year, - "supplier_group": filters.supplier_group, - "company": filters.company - }, as_dict=True) + gl.party DESC""".format(conditions=conditions), { + "fiscal_year": filters.fiscal_year, + "company": filters.company + }, as_dict=True) return columns, data @@ -79,13 +85,13 @@ def get_columns(): "fieldname": "tax_id", "label": _("Tax ID"), "fieldtype": "Data", - "width": 120 + "width": 200 }, { "fieldname": "payments", "label": _("Total Payments"), "fieldtype": "Currency", - "width": 120 + "width": 200 } ] From e816ed89e6923bc5d378ccc0440e68aa3b61d414 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Thu, 11 Feb 2021 11:12:13 +0530 Subject: [PATCH 654/728] fix: portal permission issue (#24577) --- .../setup/doctype/customer_group/customer_group.json | 11 ++++++++++- erpnext/setup/doctype/item_group/item_group.json | 11 ++++++++++- erpnext/setup/doctype/territory/territory.json | 11 ++++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/erpnext/setup/doctype/customer_group/customer_group.json b/erpnext/setup/doctype/customer_group/customer_group.json index 10f9bd0030..0e2ed9efcf 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.json +++ b/erpnext/setup/doctype/customer_group/customer_group.json @@ -139,7 +139,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-03-18 18:10:13.048492", + "modified": "2021-02-08 17:01:52.162202", "modified_by": "Administrator", "module": "Setup", "name": "Customer Group", @@ -189,6 +189,15 @@ "permlevel": 1, "read": 1, "role": "Sales Manager" + }, + { + "email": 1, + "export": 1, + "print": 1, + "report": 1, + "role": "Customer", + "select": 1, + "share": 1 } ], "search_fields": "parent_customer_group", diff --git a/erpnext/setup/doctype/item_group/item_group.json b/erpnext/setup/doctype/item_group/item_group.json index 31624edb49..e835214487 100644 --- a/erpnext/setup/doctype/item_group/item_group.json +++ b/erpnext/setup/doctype/item_group/item_group.json @@ -214,7 +214,7 @@ "is_tree": 1, "links": [], "max_attachments": 3, - "modified": "2020-12-30 12:57:38.876956", + "modified": "2021-02-08 17:02:44.951572", "modified_by": "Administrator", "module": "Setup", "name": "Item Group", @@ -271,6 +271,15 @@ "read": 1, "report": 1, "role": "Accounts User" + }, + { + "email": 1, + "export": 1, + "print": 1, + "report": 1, + "role": "Customer", + "select": 1, + "share": 1 } ], "search_fields": "parent_item_group", diff --git a/erpnext/setup/doctype/territory/territory.json b/erpnext/setup/doctype/territory/territory.json index aa8e0486f5..a25bda054b 100644 --- a/erpnext/setup/doctype/territory/territory.json +++ b/erpnext/setup/doctype/territory/territory.json @@ -123,7 +123,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-03-18 18:11:36.623555", + "modified": "2021-02-08 17:10:03.767426", "modified_by": "Administrator", "module": "Setup", "name": "Territory", @@ -166,6 +166,15 @@ { "read": 1, "role": "Maintenance User" + }, + { + "email": 1, + "export": 1, + "print": 1, + "report": 1, + "role": "Customer", + "select": 1, + "share": 1 } ], "search_fields": "parent_territory,territory_manager", From bb05acb60dce762269af1a20e786e26d3f892ef6 Mon Sep 17 00:00:00 2001 From: Kanchan Chauhan Date: Thu, 11 Feb 2021 11:18:59 +0530 Subject: [PATCH 655/728] fix(Leave Application): List view fix (#24550) --- erpnext/hr/doctype/leave_application/leave_application_list.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/hr/doctype/leave_application/leave_application_list.js b/erpnext/hr/doctype/leave_application/leave_application_list.js index cbb4b73227..a3c03b1bec 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_list.js +++ b/erpnext/hr/doctype/leave_application/leave_application_list.js @@ -1,5 +1,6 @@ frappe.listview_settings['Leave Application'] = { add_fields: ["leave_type", "employee", "employee_name", "total_leave_days", "from_date", "to_date"], + has_indicator_for_draft: 1, get_indicator: function (doc) { if (doc.status === "Approved") { return [__("Approved"), "green", "status,=,Approved"]; From 30c8873f2446fdd02250cc0b92ef13346f6f7be8 Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Thu, 11 Feb 2021 11:46:48 +0530 Subject: [PATCH 656/728] fix: validation for disabled warehouse (#24539) * fix: validation for disabled warehouse * fix: better function name * fix: validation in stock ledger entry --- erpnext/controllers/stock_controller.py | 3 ++- .../stock/doctype/stock_ledger_entry/stock_ledger_entry.py | 3 ++- erpnext/stock/utils.py | 6 +++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 4b5e347970..2ae9dc7102 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -313,7 +313,7 @@ class StockController(AccountsController): return serialized_items def validate_warehouse(self): - from erpnext.stock.utils import validate_warehouse_company + from erpnext.stock.utils import validate_warehouse_company, validate_disabled_warehouse warehouses = list(set([d.warehouse for d in self.get("items") if getattr(d, "warehouse", None)])) @@ -329,6 +329,7 @@ class StockController(AccountsController): warehouses.extend(from_warehouse) for w in warehouses: + validate_disabled_warehouse(w) validate_warehouse_company(w, self.company) def update_billing_percentage(self, update_modified=True): diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index a5c303ccb4..36d09efd1a 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -27,10 +27,11 @@ class StockLedgerEntry(Document): def validate(self): self.flags.ignore_submit_comment = True - from erpnext.stock.utils import validate_warehouse_company + from erpnext.stock.utils import validate_warehouse_company, validate_disabled_warehouse self.validate_mandatory() self.validate_item() self.validate_batch() + validate_disabled_warehouse(self.warehouse) validate_warehouse_company(self.warehouse, self.company) self.scrub_posting_time() self.validate_and_set_fiscal_year() diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 4ea7e4fcd6..0af3d90822 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _ import json -from frappe.utils import flt, cstr, nowdate, nowtime +from frappe.utils import flt, cstr, nowdate, nowtime, get_link_to_form from six import string_types @@ -284,6 +284,10 @@ def is_group_warehouse(warehouse): if frappe.db.get_value("Warehouse", warehouse, "is_group"): frappe.throw(_("Group node warehouse is not allowed to select for transactions")) +def validate_disabled_warehouse(warehouse): + if frappe.db.get_value("Warehouse", warehouse, "disabled"): + frappe.throw(_("Disabled Warehouse {0} cannot be used for this transaction.").format(get_link_to_form('Warehouse', warehouse))) + def update_included_uom_in_report(columns, result, include_uom, conversion_factors): if not include_uom or not conversion_factors: return From 15f4672ffac9ad2d77b44abe0996fba4de211042 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 11 Feb 2021 12:11:44 +0530 Subject: [PATCH 657/728] fix: pos closing valdation (#24605) --- .../pos_closing_entry/pos_closing_entry.py | 15 ++++++++++----- .../pos_invoice_merge_log.py | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py index edf3d5a574..f5224a269e 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py @@ -20,11 +20,16 @@ class POSClosingEntry(StatusUpdater): self.validate_pos_invoices() def validate_pos_closing(self): - user = frappe.get_all("POS Closing Entry", - filters = { "user": self.user, "docstatus": 1, "pos_profile": self.pos_profile }, - or_filters = { - "period_start_date": ("between", [self.period_start_date, self.period_end_date]), - "period_end_date": ("between", [self.period_start_date, self.period_end_date]) + user = frappe.db.sql(""" + SELECT name FROM `tabPOS Closing Entry` + WHERE + user = %(user)s AND docstatus = 1 AND pos_profile = %(profile)s AND + (period_start_date between %(start)s and %(end)s OR period_end_date between %(start)s and %(end)s) + """, { + 'user': self.user, + 'profile': self.pos_profile, + 'start': self.period_start_date, + 'end': self.period_end_date }) if user: diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 58409cd3c6..c88d67989b 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -212,8 +212,8 @@ def consolidate_pos_invoices(pos_invoices=[], closing_entry={}): invoice_by_customer = get_invoice_customer_map(invoices) if len(invoices) >= 5 and closing_entry: - enqueue_job(create_merge_logs, invoice_by_customer, closing_entry) closing_entry.set_status(update=True, status='Queued') + enqueue_job(create_merge_logs, invoice_by_customer, closing_entry) else: create_merge_logs(invoice_by_customer, closing_entry) @@ -225,8 +225,8 @@ def unconsolidate_pos_invoices(closing_entry): ) if len(merge_logs) >= 5: - enqueue_job(cancel_merge_logs, merge_logs, closing_entry) closing_entry.set_status(update=True, status='Queued') + enqueue_job(cancel_merge_logs, merge_logs, closing_entry) else: cancel_merge_logs(merge_logs, closing_entry) From e37a5c2646b28fd69e3fe1a00056f1ea283c937e Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 11 Feb 2021 13:10:52 +0530 Subject: [PATCH 658/728] fix: customer_currency referenced before assignment (#24607) --- erpnext/accounts/doctype/pos_invoice/pos_invoice.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 8d8babbe75..165294b24c 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -317,13 +317,14 @@ class POSInvoice(SalesInvoice): ) customer_group_price_list = frappe.db.get_value("Customer Group", customer_group, 'default_price_list') selling_price_list = customer_price_list or customer_group_price_list or profile.get('selling_price_list') + if customer_currency != profile.get('currency'): + self.set('currency', customer_currency) + else: selling_price_list = profile.get('selling_price_list') if selling_price_list: self.set('selling_price_list', selling_price_list) - if customer_currency != profile.get('currency'): - self.set('currency', customer_currency) # set pos values in items for item in self.get("items"): From aa00eb98987fb26435ce02ed10019ffa32d0ff0b Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 11 Feb 2021 13:23:01 +0530 Subject: [PATCH 659/728] fix: reposting issue with same posting date and posting time (#24570) * fix: reposting issue with same posting date and posting time * Update erpnext/stock/stock_ledger.py Co-authored-by: Nabin Hait --- erpnext/stock/stock_ledger.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 46919c8c8c..95f8c438b3 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -202,8 +202,7 @@ class update_entries_after(object): where item_code = %(item_code)s and warehouse = %(warehouse)s - and voucher_type = %(voucher_type)s - and voucher_no = %(voucher_no)s + and timestamp(posting_date, time_format(posting_time, '%H:%i:%s')) = timestamp(%(posting_date)s, time_format(%(posting_time)s, '%H:%i:%s')) order by creation ASC for update @@ -794,4 +793,4 @@ def get_future_sle_with_negative_qty(args): and qty_after_transaction + {0} < 0 order by timestamp(posting_date, posting_time) asc limit 1 - """.format(args.actual_qty), args, as_dict=1) \ No newline at end of file + """.format(args.actual_qty), args, as_dict=1) From 47f784aa9cbf76d226bc0bca6a37f6e07618079f Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Thu, 11 Feb 2021 13:23:51 +0530 Subject: [PATCH 660/728] fix: Lead not getting linked to call logs (#24593) Co-authored-by: Nabin Hait --- erpnext/crm/doctype/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/utils.py b/erpnext/crm/doctype/utils.py index 4ccd9bd73b..f244daffea 100644 --- a/erpnext/crm/doctype/utils.py +++ b/erpnext/crm/doctype/utils.py @@ -78,7 +78,9 @@ def get_scheduled_employees_for_popup(communication_medium): def strip_number(number): if not number: return - # strip 0 from the start of the number for proper number comparisions + # strip + and 0 from the start of the number for proper number comparisions + # eg. +7888383332 should match with 7888383332 # eg. 07888383332 should match with 7888383332 + number = number.lstrip('+') number = number.lstrip('0') return number From aa235b36e05c8c058407c5f0366e63cf40d56a3e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 11 Feb 2021 13:26:53 +0530 Subject: [PATCH 661/728] Update erpnext/controllers/accounts_controller.py Co-authored-by: Marica --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index d341030aba..db6484a606 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1319,7 +1319,7 @@ def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child for field in ("item_code", "item_name", "description", "item_group"): child_item.update({field: item.get(field)}) date_fieldname = "delivery_date" if child_doctype == "Sales Order Item" else "schedule_date" - child_item.date_fieldname = trans_item.get(date_fieldname) or p_doc.date_fieldname + child_item.update({date_fieldname: trans_item.get(date_fieldname) or p_doc.date_fieldname}) child_item.uom = trans_item.get("uom") or item.stock_uom conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor From a46a83b476f9dcfc040c5cc9fb09b9883bf947b9 Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Thu, 11 Feb 2021 13:41:10 +0530 Subject: [PATCH 662/728] fix: Assignment Rule Unassign Condition doesn't work (#24551) Co-authored-by: Marica Co-authored-by: Nabin Hait --- erpnext/selling/doctype/customer/customer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index bf8b7fc128..c452594608 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -126,7 +126,9 @@ class Customer(TransactionBase): '''If Customer created from Lead, update lead status to "Converted" update Customer link in Quotation, Opportunity''' if self.lead_name: - frappe.db.set_value('Lead', self.lead_name, 'status', 'Converted', update_modified=False) + lead = frappe.get_doc('Lead', self.lead_name) + lead.status = 'Converted' + lead.save() def create_lead_address_contact(self): if self.lead_name: From 0681a480c2d92e8dabbc0f91b7c39888723fe749 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 11 Feb 2021 14:06:15 +0530 Subject: [PATCH 663/728] feat(pos): mpesa related fixes & additions (#24306) * fix: switching of mode of payments * feat: transaction limit for mpesa integration * feat: resend payment request if one fails * feat: make new request only for failed ones * fix: invalid amount for mpesa request * fix: payment successful message not shown * fix: url and business shortcode for live env * fix: duplicate items validation for pos invoices * fix: pos closing entry queued status * fix: peroid end date for amended pos closing --- .../payment_request/payment_request.py | 50 +++-- .../payment_request/test_payment_request.py | 12 +- .../pos_closing_entry/pos_closing_entry.js | 2 +- .../doctype/pos_invoice/pos_invoice.js | 41 +++- .../doctype/pos_invoice/pos_invoice.py | 56 ++++-- erpnext/controllers/selling_controller.py | 6 +- .../doctype/mpesa_settings/mpesa_connector.py | 8 +- .../mpesa_settings/mpesa_settings.json | 18 +- .../doctype/mpesa_settings/mpesa_settings.py | 115 ++++++++--- .../mpesa_settings/test_mpesa_settings.py | 180 ++++++++++++++---- .../page/point_of_sale/pos_item_cart.js | 2 +- .../selling/page/point_of_sale/pos_payment.js | 74 ++++--- 12 files changed, 433 insertions(+), 131 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 1b97050eb1..53ac996290 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -3,6 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals +import json import frappe from frappe import _ from frappe.model.document import Document @@ -82,18 +83,37 @@ class PaymentRequest(Document): self.make_communication_entry() elif self.payment_channel == "Phone": - controller = get_payment_gateway_controller(self.payment_gateway) - payment_record = dict( - reference_doctype="Payment Request", - reference_docname=self.name, - payment_reference=self.reference_name, - grand_total=self.grand_total, - sender=self.email_to, - currency=self.currency, - payment_gateway=self.payment_gateway - ) - controller.validate_transaction_currency(self.currency) - controller.request_for_payment(**payment_record) + self.request_phone_payment() + + def request_phone_payment(self): + controller = get_payment_gateway_controller(self.payment_gateway) + request_amount = self.get_request_amount() + + payment_record = dict( + reference_doctype="Payment Request", + reference_docname=self.name, + payment_reference=self.reference_name, + request_amount=request_amount, + sender=self.email_to, + currency=self.currency, + payment_gateway=self.payment_gateway + ) + + controller.validate_transaction_currency(self.currency) + controller.request_for_payment(**payment_record) + + def get_request_amount(self): + data_of_completed_requests = frappe.get_all("Integration Request", filters={ + 'reference_doctype': self.doctype, + 'reference_docname': self.name, + 'status': 'Completed' + }, pluck="data") + + if not data_of_completed_requests: + return self.grand_total + + request_amounts = sum([json.loads(d).get('request_amount') for d in data_of_completed_requests]) + return request_amounts def on_cancel(self): self.check_if_payment_entry_exists() @@ -351,8 +371,8 @@ def make_payment_request(**args): if args.order_type == "Shopping Cart" or args.mute_email: pr.flags.mute_email = True + pr.insert(ignore_permissions=True) if args.submit_doc: - pr.insert(ignore_permissions=True) pr.submit() if args.order_type == "Shopping Cart": @@ -412,8 +432,8 @@ def get_existing_payment_request_amount(ref_dt, ref_dn): def get_gateway_details(args): """return gateway and payment account of default payment gateway""" - if args.get("payment_gateway"): - return get_payment_gateway_account(args.get("payment_gateway")) + if args.get("payment_gateway_account"): + return get_payment_gateway_account(args.get("payment_gateway_account")) if args.order_type == "Shopping Cart": payment_gateway_account = frappe.get_doc("Shopping Cart Settings").payment_gateway_account diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 8a10e2cbd9..5eba62c0b3 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -45,7 +45,8 @@ class TestPaymentRequest(unittest.TestCase): def test_payment_request_linkings(self): so_inr = make_sales_order(currency="INR") - pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com") + pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com", + payment_gateway_account="_Test Gateway - INR") self.assertEqual(pr.reference_doctype, "Sales Order") self.assertEqual(pr.reference_name, so_inr.name) @@ -54,7 +55,8 @@ class TestPaymentRequest(unittest.TestCase): conversion_rate = get_exchange_rate("USD", "INR") si_usd = create_sales_invoice(currency="USD", conversion_rate=conversion_rate) - pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com") + pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com", + payment_gateway_account="_Test Gateway - USD") self.assertEqual(pr.reference_doctype, "Sales Invoice") self.assertEqual(pr.reference_name, si_usd.name) @@ -68,7 +70,7 @@ class TestPaymentRequest(unittest.TestCase): so_inr = make_sales_order(currency="INR") pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com", - mute_email=1, submit_doc=1, return_doc=1) + mute_email=1, payment_gateway_account="_Test Gateway - INR", submit_doc=1, return_doc=1) pe = pr.set_as_paid() so_inr = frappe.get_doc("Sales Order", so_inr.name) @@ -79,7 +81,7 @@ class TestPaymentRequest(unittest.TestCase): currency="USD", conversion_rate=50) pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com", - mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1, return_doc=1) + mute_email=1, payment_gateway_account="_Test Gateway - USD", submit_doc=1, return_doc=1) pe = pr.set_as_paid() @@ -106,7 +108,7 @@ class TestPaymentRequest(unittest.TestCase): currency="USD", conversion_rate=50) pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com", - mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1, return_doc=1) + mute_email=1, payment_gateway_account="_Test Gateway - USD", submit_doc=1, return_doc=1) pe = pr.create_payment_entry() pr.load_from_db() diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js index 73367fd736..9ea616f8e7 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js @@ -21,7 +21,7 @@ frappe.ui.form.on('POS Closing Entry', { return { filters: { 'status': 'Open', 'docstatus': 1 } }; }); - if (frm.doc.docstatus === 0) frm.set_value("period_end_date", frappe.datetime.now_datetime()); + if (frm.doc.docstatus === 0 && !frm.doc.amended_from) frm.set_value("period_end_date", frappe.datetime.now_datetime()); if (frm.doc.docstatus === 1) set_html_data(frm); }, diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index 07c8e44e77..493bd44802 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -195,18 +195,43 @@ frappe.ui.form.on('POS Invoice', { }, request_for_payment: function (frm) { + if (!frm.doc.contact_mobile) { + frappe.throw(__('Please enter mobile number first.')); + } + frm.dirty(); frm.save().then(() => { - frappe.dom.freeze(); - frappe.call({ - method: 'create_payment_request', - doc: frm.doc, - }) + frappe.dom.freeze(__('Waiting for payment...')); + frappe + .call({ + method: 'create_payment_request', + doc: frm.doc + }) .fail(() => { frappe.dom.unfreeze(); - frappe.msgprint('Payment request failed'); + frappe.msgprint(__('Payment request failed')); }) - .then(() => { - frappe.msgprint('Payment request sent successfully'); + .then(({ message }) => { + const payment_request_name = message.name; + setTimeout(() => { + frappe.db.get_value('Payment Request', payment_request_name, ['status', 'grand_total']).then(({ message }) => { + if (message.status != 'Paid') { + frappe.dom.unfreeze(); + frappe.msgprint({ + message: __('Payment Request took too long to respond. Please try requesting for payment again.'), + title: __('Request Timeout') + }); + } else if (frappe.dom.freeze_count != 0) { + frappe.dom.unfreeze(); + cur_frm.reload_doc(); + cur_pos.payment.events.submit_invoice(); + + frappe.show_alert({ + message: __("Payment of {0} received successfully.", [format_currency(message.grand_total, frm.doc.currency, 0)]), + indicator: 'green' + }); + } + }); + }, 60000); }); }); } diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 165294b24c..94573f9df3 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -384,22 +384,48 @@ class POSInvoice(SalesInvoice): if not self.contact_mobile: frappe.throw(_("Please enter the phone number first")) - payment_gateway = frappe.db.get_value("Payment Gateway Account", { - "payment_account": pay.account, - }) - record = { - "payment_gateway": payment_gateway, - "dt": "POS Invoice", - "dn": self.name, - "payment_request_type": "Inward", - "party_type": "Customer", - "party": self.customer, - "mode_of_payment": pay.mode_of_payment, - "recipient_id": self.contact_mobile, - "submit_doc": True - } + pay_req = self.get_existing_payment_request(pay) + if not pay_req: + pay_req = self.get_new_payment_request(pay) + pay_req.submit() + else: + pay_req.request_phone_payment() - return make_payment_request(**record) + return pay_req + + def get_new_payment_request(self, mop): + payment_gateway_account = frappe.db.get_value("Payment Gateway Account", { + "payment_account": mop.account, + }, ["name"]) + + args = { + "dt": "POS Invoice", + "dn": self.name, + "recipient_id": self.contact_mobile, + "mode_of_payment": mop.mode_of_payment, + "payment_gateway_account": payment_gateway_account, + "payment_request_type": "Inward", + "party_type": "Customer", + "party": self.customer, + "return_doc": True + } + return make_payment_request(**args) + + def get_existing_payment_request(self, pay): + payment_gateway_account = frappe.db.get_value("Payment Gateway Account", { + "payment_account": pay.account, + }, ["name"]) + + args = { + 'doctype': 'Payment Request', + 'reference_doctype': 'POS Invoice', + 'reference_name': self.name, + 'payment_gateway_account': payment_gateway_account, + 'email_to': self.contact_mobile + } + pr = frappe.db.exists(args) + if pr: + return frappe.get_doc('Payment Request', pr[0][0]) @frappe.whitelist() def get_stock_availability(item_code, warehouse): diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 6abfe04db9..c61b67b0a4 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -446,9 +446,13 @@ class SellingController(StockController): check_list, chk_dupl_itm = [], [] if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")): return + if self.doctype == "Sales Invoice" and self.is_consolidated: + return + if self.doctype == "POS Invoice": + return for d in self.get('items'): - if self.doctype in ["POS Invoice","Sales Invoice"]: + if self.doctype == "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": diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py index d33b0a7089..554c6b0eb0 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py @@ -5,7 +5,7 @@ import datetime class MpesaConnector(): def __init__(self, env="sandbox", app_key=None, app_secret=None, sandbox_url="https://sandbox.safaricom.co.ke", - live_url="https://safaricom.co.ke"): + live_url="https://api.safaricom.co.ke"): """Setup configuration for Mpesa connector and generate new access token.""" self.env = env self.app_key = app_key @@ -102,14 +102,14 @@ class MpesaConnector(): "BusinessShortCode": business_shortcode, "Password": encoded.decode("utf-8"), "Timestamp": time, - "TransactionType": "CustomerPayBillOnline", "Amount": amount, "PartyA": int(phone_number), - "PartyB": business_shortcode, + "PartyB": reference_code, "PhoneNumber": int(phone_number), "CallBackURL": callback_url, "AccountReference": reference_code, - "TransactionDesc": description + "TransactionDesc": description, + "TransactionType": "CustomerPayBillOnline" if self.env == "sandbox" else "CustomerBuyGoodsOnline" } headers = {'Authorization': 'Bearer {0}'.format(self.authentication_token), 'Content-Type': "application/json"} diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json index fc7b310c08..407f82616f 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json @@ -11,8 +11,10 @@ "consumer_secret", "initiator_name", "till_number", + "transaction_limit", "sandbox", "column_break_4", + "business_shortcode", "online_passkey", "security_credential", "get_account_balance", @@ -84,10 +86,24 @@ "fieldname": "get_account_balance", "fieldtype": "Button", "label": "Get Account Balance" + }, + { + "depends_on": "eval:(doc.sandbox==0)", + "fieldname": "business_shortcode", + "fieldtype": "Data", + "label": "Business Shortcode", + "mandatory_depends_on": "eval:(doc.sandbox==0)" + }, + { + "default": "150000", + "fieldname": "transaction_limit", + "fieldtype": "Float", + "label": "Transaction Limit", + "non_negative": 1 } ], "links": [], - "modified": "2020-09-25 20:21:38.215494", + "modified": "2021-01-29 12:02:16.106942", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Mpesa Settings", diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py index 1cad84dcde..b5718026c1 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py @@ -33,13 +33,34 @@ class MpesaSettings(Document): create_mode_of_payment('Mpesa-' + self.payment_gateway_name, payment_type="Phone") def request_for_payment(self, **kwargs): - if frappe.flags.in_test: - from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import get_payment_request_response_payload - response = frappe._dict(get_payment_request_response_payload()) - else: - response = frappe._dict(generate_stk_push(**kwargs)) + args = frappe._dict(kwargs) + request_amounts = self.split_request_amount_according_to_transaction_limit(args) - self.handle_api_response("CheckoutRequestID", kwargs, response) + for i, amount in enumerate(request_amounts): + args.request_amount = amount + if frappe.flags.in_test: + from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import get_payment_request_response_payload + response = frappe._dict(get_payment_request_response_payload(amount)) + else: + response = frappe._dict(generate_stk_push(**args)) + + self.handle_api_response("CheckoutRequestID", args, response) + + def split_request_amount_according_to_transaction_limit(self, args): + request_amount = args.request_amount + if request_amount > self.transaction_limit: + # make multiple requests + request_amounts = [] + requests_to_be_made = frappe.utils.ceil(request_amount / self.transaction_limit) # 480/150 = ceil(3.2) = 4 + for i in range(requests_to_be_made): + amount = self.transaction_limit + if i == requests_to_be_made - 1: + amount = request_amount - (self.transaction_limit * i) # for 4th request, 480 - (150 * 3) = 30 + request_amounts.append(amount) + else: + request_amounts = [request_amount] + + return request_amounts def get_account_balance_info(self): payload = dict( @@ -67,7 +88,8 @@ class MpesaSettings(Document): req_name = getattr(response, global_id) error = None - create_request_log(request_dict, "Host", "Mpesa", req_name, error) + if not frappe.db.exists('Integration Request', req_name): + create_request_log(request_dict, "Host", "Mpesa", req_name, error) if error: frappe.throw(_(getattr(response, "errorMessage")), title=_("Transaction Error")) @@ -80,6 +102,8 @@ def generate_stk_push(**kwargs): mpesa_settings = frappe.get_doc("Mpesa Settings", args.payment_gateway[6:]) env = "production" if not mpesa_settings.sandbox else "sandbox" + # for sandbox, business shortcode is same as till number + business_shortcode = mpesa_settings.business_shortcode if env == "production" else mpesa_settings.till_number connector = MpesaConnector(env=env, app_key=mpesa_settings.consumer_key, @@ -87,10 +111,12 @@ def generate_stk_push(**kwargs): mobile_number = sanitize_mobile_number(args.sender) - response = connector.stk_push(business_shortcode=mpesa_settings.till_number, - passcode=mpesa_settings.get_password("online_passkey"), amount=args.grand_total, + response = connector.stk_push( + business_shortcode=business_shortcode, amount=args.request_amount, + passcode=mpesa_settings.get_password("online_passkey"), callback_url=callback_url, reference_code=mpesa_settings.till_number, - phone_number=mobile_number, description="POS Payment") + phone_number=mobile_number, description="POS Payment" + ) return response @@ -108,29 +134,72 @@ def verify_transaction(**kwargs): transaction_response = frappe._dict(kwargs["Body"]["stkCallback"]) checkout_id = getattr(transaction_response, "CheckoutRequestID", "") - request = frappe.get_doc("Integration Request", checkout_id) - transaction_data = frappe._dict(loads(request.data)) + integration_request = frappe.get_doc("Integration Request", checkout_id) + transaction_data = frappe._dict(loads(integration_request.data)) + total_paid = 0 # for multiple integration request made against a pos invoice + success = False # for reporting successfull callback to point of sale ui if transaction_response['ResultCode'] == 0: - if request.reference_doctype and request.reference_docname: + if integration_request.reference_doctype and integration_request.reference_docname: try: - doc = frappe.get_doc(request.reference_doctype, - request.reference_docname) - doc.run_method("on_payment_authorized", 'Completed') - item_response = transaction_response["CallbackMetadata"]["Item"] + amount = fetch_param_value(item_response, "Amount", "Name") mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name") - frappe.db.set_value("POS Invoice", doc.reference_name, "mpesa_receipt_number", mpesa_receipt) - request.handle_success(transaction_response) + pr = frappe.get_doc(integration_request.reference_doctype, integration_request.reference_docname) + + mpesa_receipts, completed_payments = get_completed_integration_requests_info( + integration_request.reference_doctype, + integration_request.reference_docname, + checkout_id + ) + + total_paid = amount + sum(completed_payments) + mpesa_receipts = ', '.join(mpesa_receipts + [mpesa_receipt]) + + if total_paid >= pr.grand_total: + pr.run_method("on_payment_authorized", 'Completed') + success = True + + frappe.db.set_value("POS Invoice", pr.reference_name, "mpesa_receipt_number", mpesa_receipts) + integration_request.handle_success(transaction_response) except Exception: - request.handle_failure(transaction_response) + integration_request.handle_failure(transaction_response) frappe.log_error(frappe.get_traceback()) else: - request.handle_failure(transaction_response) + integration_request.handle_failure(transaction_response) - frappe.publish_realtime('process_phone_payment', doctype="POS Invoice", - docname=transaction_data.payment_reference, user=request.owner, message=transaction_response) + frappe.publish_realtime( + event='process_phone_payment', + doctype="POS Invoice", + docname=transaction_data.payment_reference, + user=integration_request.owner, + message={ + 'amount': total_paid, + 'success': success, + 'failure_message': transaction_response["ResultDesc"] if transaction_response['ResultCode'] != 0 else '' + }, + ) + +def get_completed_integration_requests_info(reference_doctype, reference_docname, checkout_id): + output_of_other_completed_requests = frappe.get_all("Integration Request", filters={ + 'name': ['!=', checkout_id], + 'reference_doctype': reference_doctype, + 'reference_docname': reference_docname, + 'status': 'Completed' + }, pluck="output") + + mpesa_receipts, completed_payments = [], [] + + for out in output_of_other_completed_requests: + out = frappe._dict(loads(out)) + item_response = out["CallbackMetadata"]["Item"] + completed_amount = fetch_param_value(item_response, "Amount", "Name") + completed_mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name") + completed_payments.append(completed_amount) + mpesa_receipts.append(completed_mpesa_receipt) + + return mpesa_receipts, completed_payments def get_account_balance(request_payload): """Call account balance API to send the request to the Mpesa Servers.""" diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py index 49f6d95a6e..29487962f6 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py @@ -9,6 +9,10 @@ from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings import p from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice class TestMpesaSettings(unittest.TestCase): + def tearDown(self): + frappe.db.sql('delete from `tabMpesa Settings`') + frappe.db.sql('delete from `tabIntegration Request` where integration_request_service = "Mpesa"') + def test_creation_of_payment_gateway(self): create_mpesa_settings(payment_gateway_name="_Test") @@ -40,6 +44,8 @@ class TestMpesaSettings(unittest.TestCase): } })) + integration_request.delete() + def test_processing_of_callback_payload(self): create_mpesa_settings(payment_gateway_name="Payment") mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account") @@ -56,10 +62,16 @@ class TestMpesaSettings(unittest.TestCase): # test payment request creation self.assertEquals(pr.payment_gateway, "Mpesa-Payment") - callback_response = get_payment_callback_payload() + # submitting payment request creates integration requests with random id + integration_req_ids = frappe.get_all("Integration Request", filters={ + 'reference_doctype': pr.doctype, + 'reference_docname': pr.name, + }, pluck="name") + + callback_response = get_payment_callback_payload(Amount=500, CheckoutRequestID=integration_req_ids[0]) verify_transaction(**callback_response) # test creation of integration request - integration_request = frappe.get_doc("Integration Request", "ws_CO_061020201133231972") + integration_request = frappe.get_doc("Integration Request", integration_req_ids[0]) # test integration request creation and successful update of the status on receiving callback response self.assertTrue(integration_request) @@ -69,8 +81,120 @@ class TestMpesaSettings(unittest.TestCase): integration_request.reload() self.assertEquals(pos_invoice.mpesa_receipt_number, "LGR7OWQX0R") self.assertEquals(integration_request.status, "Completed") + + frappe.db.set_value("Customer", "_Test Customer", "default_currency", "") + integration_request.delete() + pr.reload() + pr.cancel() + pr.delete() + pos_invoice.delete() + + def test_processing_of_multiple_callback_payload(self): + create_mpesa_settings(payment_gateway_name="Payment") + mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account") + frappe.db.set_value("Account", mpesa_account, "account_currency", "KES") + frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500") + frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES") + + pos_invoice = create_pos_invoice(do_not_submit=1) + pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 1000}) + pos_invoice.contact_mobile = "093456543894" + pos_invoice.currency = "KES" + pos_invoice.save() + + pr = pos_invoice.create_payment_request() + # test payment request creation + self.assertEquals(pr.payment_gateway, "Mpesa-Payment") + + # submitting payment request creates integration requests with random id + integration_req_ids = frappe.get_all("Integration Request", filters={ + 'reference_doctype': pr.doctype, + 'reference_docname': pr.name, + }, pluck="name") + + # create random receipt nos and send it as response to callback handler + mpesa_receipt_numbers = [frappe.utils.random_string(5) for d in integration_req_ids] + + integration_requests = [] + for i in range(len(integration_req_ids)): + callback_response = get_payment_callback_payload( + Amount=500, + CheckoutRequestID=integration_req_ids[i], + MpesaReceiptNumber=mpesa_receipt_numbers[i] + ) + # handle response manually + verify_transaction(**callback_response) + # test completion of integration request + integration_request = frappe.get_doc("Integration Request", integration_req_ids[i]) + self.assertEquals(integration_request.status, "Completed") + integration_requests.append(integration_request) + + # check receipt number once all the integration requests are completed + pos_invoice.reload() + self.assertEquals(pos_invoice.mpesa_receipt_number, ', '.join(mpesa_receipt_numbers)) frappe.db.set_value("Customer", "_Test Customer", "default_currency", "") + [d.delete() for d in integration_requests] + pr.reload() + pr.cancel() + pr.delete() + pos_invoice.delete() + + def test_processing_of_only_one_succes_callback_payload(self): + create_mpesa_settings(payment_gateway_name="Payment") + mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account") + frappe.db.set_value("Account", mpesa_account, "account_currency", "KES") + frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500") + frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES") + + pos_invoice = create_pos_invoice(do_not_submit=1) + pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 1000}) + pos_invoice.contact_mobile = "093456543894" + pos_invoice.currency = "KES" + pos_invoice.save() + + pr = pos_invoice.create_payment_request() + # test payment request creation + self.assertEquals(pr.payment_gateway, "Mpesa-Payment") + + # submitting payment request creates integration requests with random id + integration_req_ids = frappe.get_all("Integration Request", filters={ + 'reference_doctype': pr.doctype, + 'reference_docname': pr.name, + }, pluck="name") + + # create random receipt nos and send it as response to callback handler + mpesa_receipt_numbers = [frappe.utils.random_string(5) for d in integration_req_ids] + + callback_response = get_payment_callback_payload( + Amount=500, + CheckoutRequestID=integration_req_ids[0], + MpesaReceiptNumber=mpesa_receipt_numbers[0] + ) + # handle response manually + verify_transaction(**callback_response) + # test completion of integration request + integration_request = frappe.get_doc("Integration Request", integration_req_ids[0]) + self.assertEquals(integration_request.status, "Completed") + + # now one request is completed + # second integration request fails + # now retrying payment request should make only one integration request again + pr = pos_invoice.create_payment_request() + new_integration_req_ids = frappe.get_all("Integration Request", filters={ + 'reference_doctype': pr.doctype, + 'reference_docname': pr.name, + 'name': ['not in', integration_req_ids] + }, pluck="name") + + self.assertEquals(len(new_integration_req_ids), 1) + + frappe.db.set_value("Customer", "_Test Customer", "default_currency", "") + frappe.db.sql("delete from `tabIntegration Request` where integration_request_service = 'Mpesa'") + pr.reload() + pr.cancel() + pr.delete() + pos_invoice.delete() def create_mpesa_settings(payment_gateway_name="Express"): if frappe.db.exists("Mpesa Settings", payment_gateway_name): @@ -160,16 +284,19 @@ def get_test_account_balance_response(): } } -def get_payment_request_response_payload(): +def get_payment_request_response_payload(Amount=500): """Response received after successfully calling the stk push process request API.""" + + CheckoutRequestID = frappe.utils.random_string(10) + return { "MerchantRequestID": "8071-27184008-1", - "CheckoutRequestID": "ws_CO_061020201133231972", + "CheckoutRequestID": CheckoutRequestID, "ResultCode": 0, "ResultDesc": "The service request is processed successfully.", "CallbackMetadata": { "Item": [ - { "Name": "Amount", "Value": 500.0 }, + { "Name": "Amount", "Value": Amount }, { "Name": "MpesaReceiptNumber", "Value": "LGR7OWQX0R" }, { "Name": "TransactionDate", "Value": 20201006113336 }, { "Name": "PhoneNumber", "Value": 254723575670 } @@ -177,41 +304,26 @@ def get_payment_request_response_payload(): } } - -def get_payment_callback_payload(): +def get_payment_callback_payload(Amount=500, CheckoutRequestID="ws_CO_061020201133231972", MpesaReceiptNumber="LGR7OWQX0R"): """Response received from the server as callback after calling the stkpush process request API.""" return { "Body":{ - "stkCallback":{ - "MerchantRequestID":"19465-780693-1", - "CheckoutRequestID":"ws_CO_061020201133231972", - "ResultCode":0, - "ResultDesc":"The service request is processed successfully.", - "CallbackMetadata":{ - "Item":[ - { - "Name":"Amount", - "Value":500 - }, - { - "Name":"MpesaReceiptNumber", - "Value":"LGR7OWQX0R" - }, - { - "Name":"Balance" - }, - { - "Name":"TransactionDate", - "Value":20170727154800 - }, - { - "Name":"PhoneNumber", - "Value":254721566839 + "stkCallback":{ + "MerchantRequestID":"19465-780693-1", + "CheckoutRequestID":CheckoutRequestID, + "ResultCode":0, + "ResultDesc":"The service request is processed successfully.", + "CallbackMetadata":{ + "Item":[ + { "Name":"Amount", "Value":Amount }, + { "Name":"MpesaReceiptNumber", "Value":MpesaReceiptNumber }, + { "Name":"Balance" }, + { "Name":"TransactionDate", "Value":20170727154800 }, + { "Name":"PhoneNumber", "Value":254721566839 } + ] } - ] } } - } } def get_account_balance_callback_payload(): 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 f753b6d5c5..044e80357d 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -477,7 +477,7 @@ erpnext.PointOfSale.ItemCart = class { const taxes = frm.doc.taxes.map(t => { return { description: t.description, rate: t.rate - } + }; }); this.render_taxes(frm.doc.total_taxes_and_charges, taxes); } diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index c9b8ad9afe..bcbac3b4be 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -153,32 +153,23 @@ erpnext.PointOfSale.Payment = class { me.$payment_modes.find(`.${mode}-amount`).css('display', 'none'); me.$payment_modes.find(`.${mode}-name`).css('display', 'inline'); - const doc = me.events.get_frm().doc; 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) : ''; + me.auto_set_remaining_amount(); } }); - frappe.realtime.on("process_phone_payment", function(data) { - frappe.dom.unfreeze(); - cur_frm.reload_doc(); - let message = data["ResultDesc"]; - let title = __("Payment Failed"); + frappe.ui.form.on('POS Invoice', 'contact_mobile', (frm) => { + const contact = frm.doc.contact_mobile; + const request_button = $(this.request_for_payment_field.$input[0]); + if (contact) { + request_button.removeClass('btn-default').addClass('btn-primary'); + } else { + request_button.removeClass('btn-primary').addClass('btn-default'); + } + }); - if (data["ResultCode"] == 0) { - title = __("Payment Received"); - $('.btn.btn-xs.btn-default[data-fieldname=request_for_payment]').html(`Payment Received`); - me.events.submit_invoice(); - } - - frappe.msgprint({ - "message": message, - "title": title - }); - }); + this.setup_listener_for_payments(); this.$payment_modes.on('click', '.shortcut', () => { const value = $(this).attr('data-value'); @@ -224,6 +215,41 @@ erpnext.PointOfSale.Payment = class { }); } + setup_listener_for_payments() { + frappe.realtime.on("process_phone_payment", (data) => { + const doc = this.events.get_frm().doc; + const { response, amount, success, failure_message } = data; + let message, title; + + if (success) { + title = __("Payment Received"); + if (amount >= doc.grand_total) { + frappe.dom.unfreeze(); + message = __("Payment of {0} received successfully.", [format_currency(amount, doc.currency, 0)]); + this.events.submit_invoice(); + cur_frm.reload_doc(); + + } else { + message = __("Payment of {0} received successfully. Waiting for other requests to complete...", [format_currency(amount, doc.currency, 0)]); + } + } else if (failure_message) { + message = failure_message; + title = __("Payment Failed"); + } + + frappe.msgprint({ "message": message, "title": title }); + }); + } + + auto_set_remaining_amount() { + const doc = this.events.get_frm().doc; + const remaining_amount = doc.grand_total - doc.paid_amount; + const current_value = this.selected_mode ? this.selected_mode.get_value() : undefined; + if (!current_value && remaining_amount > 0 && this.selected_mode) { + this.selected_mode.set_value(remaining_amount); + } + } + attach_shortcuts() { const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl'; this.$component.find('.submit-order-btn').attr("title", `${ctrl_label}+Enter`); @@ -333,9 +359,11 @@ erpnext.PointOfSale.Payment = class { fieldtype: 'Currency', placeholder: __('Enter {0} amount.', [p.mode_of_payment]), onchange: function() { - if (this.value || this.value == 0) { - frappe.model.set_value(p.doctype, p.name, 'amount', flt(this.value)) - .then(() => me.update_totals_section()); + const current_value = frappe.model.get_value(p.doctype, p.name, 'amount'); + if (current_value != this.value) { + frappe.model + .set_value(p.doctype, p.name, 'amount', flt(this.value)) + .then(() => me.update_totals_section()) const formatted_currency = format_currency(this.value, currency); me.$payment_modes.find(`.${mode}-amount`).html(formatted_currency); From f25ab6f826b178364cfcfd4c725a220f5095ba8f Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 11 Feb 2021 17:50:57 +0530 Subject: [PATCH 664/728] fix: validate cancellation only if irn generated (#24608) --- erpnext/regional/india/e_invoice/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index 5f701f2aa6..e016c81409 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -37,7 +37,7 @@ def validate_einvoice_fields(doc): elif doc.docstatus == 1 and doc._action == 'submit' and not doc.irn: frappe.throw(_('You must generate IRN before submitting the document.'), title=_('Missing IRN')) - elif doc.docstatus == 2 and doc._action == 'cancel' and not doc.irn_cancelled: + elif doc.irn and doc.docstatus == 2 and doc._action == 'cancel' and not doc.irn_cancelled: frappe.throw(_('You must cancel IRN before cancelling the document.'), title=_('Cancel Not Allowed')) def raise_document_name_too_long_error(): From 861522709012c94232c0fbdc28399ecbe5d4e878 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 11 Feb 2021 18:51:35 +0530 Subject: [PATCH 665/728] fix: Shopping cart breaks without Payment Gateway Account --- .../shopping_cart_settings.json | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json index 702583aead..3691721302 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json @@ -26,10 +26,10 @@ "quotation_series", "section_break_8", "enable_checkout", - "payment_success_url", - "column_break_11", "save_quotations_as_draft", - "payment_gateway_account" + "column_break_11", + "payment_gateway_account", + "payment_success_url" ], "fields": [ { @@ -143,10 +143,12 @@ }, { "default": "Orders", + "depends_on": "enable_checkout", "description": "After payment completion redirect user to selected page.", "fieldname": "payment_success_url", "fieldtype": "Select", "label": "Payment Success Url", + "mandatory_depends_on": "enable_checkout", "options": "\nOrders\nInvoices\nMy Account" }, { @@ -154,9 +156,11 @@ "fieldtype": "Column Break" }, { + "depends_on": "enable_checkout", "fieldname": "payment_gateway_account", "fieldtype": "Link", "label": "Payment Gateway Account", + "mandatory_depends_on": "enable_checkout", "options": "Payment Gateway Account" }, { @@ -186,7 +190,7 @@ "idx": 1, "issingle": 1, "links": [], - "modified": "2021-02-01 18:18:54.606535", + "modified": "2021-02-11 18:48:30.433058", "modified_by": "Administrator", "module": "Shopping Cart", "name": "Shopping Cart Settings", From e2d0715cdf0b785c6f9f0c5b00ad4a92c51b54ef Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 11 Feb 2021 19:06:10 +0530 Subject: [PATCH 666/728] fix: Server side validation for missing payment gateway account --- erpnext/shopping_cart/cart.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 3c9f84982b..681d161edc 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -462,6 +462,9 @@ def get_party(user=None): return customer def get_debtors_account(cart_settings): + if not cart_settings.payment_gateway_account: + frappe.throw(_("Payment Gateway Account not set"), _("Mandatory")) + payment_gateway_account_currency = \ frappe.get_doc("Payment Gateway Account", cart_settings.payment_gateway_account).currency From 0a7939277c6c3c697c1f6b9e7c9c10a2e546c835 Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Thu, 11 Feb 2021 20:11:06 +0530 Subject: [PATCH 667/728] fix: dynamic links for reports (#24462) * fix: dynamic links for reports * fix: revert changes for payment period report * fix: link doctypes --- .../bank_clearance_summary.py | 54 +++++++++++++++---- .../prospects_engaged_but_not_converted.py | 53 ++++++++++++++---- 2 files changed, 89 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py index 0861b20f14..79b0a6f30e 100644 --- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py +++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py @@ -15,15 +15,51 @@ def execute(filters=None): return columns, data def get_columns(): - return [ - _("Payment Document") + "::130", - _("Payment Entry") + ":Dynamic Link/"+_("Payment Document")+":110", - _("Posting Date") + ":Date:100", - _("Cheque/Reference No") + "::120", - _("Clearance Date") + ":Date:100", - _("Against Account") + ":Link/Account:170", - _("Amount") + ":Currency:120" - ] + columns = [{ + "label": _("Payment Document Type"), + "fieldname": "payment_document_type", + "fieldtype": "Link", + "options": "Doctype", + "width": 130 + }, + { + "label": _("Payment Entry"), + "fieldname": "payment_entry", + "fieldtype": "Dynamic Link", + "options": "payment_document_type", + "width": 140 + }, + { + "label": _("Posting Date"), + "fieldname": "posting_date", + "fieldtype": "Date", + "width": 100 + }, + { + "label": _("Cheque/Reference No"), + "fieldname": "cheque_no", + "width": 120 + }, + { + "label": _("Clearance Date"), + "fieldname": "clearance_date", + "fieldtype": "Date", + "width": 100 + }, + { + "label": _("Against Account"), + "fieldname": "against", + "fieldtype": "Link", + "options": "Account", + "width": 170 + }, + { + "label": _("Amount"), + "fieldname": "amount", + "width": 120 + }] + + return columns def get_conditions(filters): conditions = "" diff --git a/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py b/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py index b538a58189..3a9d57d607 100644 --- a/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py +++ b/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py @@ -19,15 +19,50 @@ def set_defaut_value_for_filters(filters): if not filters.get('lead_age'): filters["lead_age"] = 60 def get_columns(): - return [ - _("Lead") + ":Link/Lead:100", - _("Name") + "::100", - _("Organization") + "::100", - _("Reference Document") + "::150", - _("Reference Name") + ":Dynamic Link/"+_("Reference Document")+":120", - _("Last Communication") + ":Data:200", - _("Last Communication Date") + ":Date:180" - ] + columns = [{ + "label": _("Lead"), + "fieldname": "lead", + "fieldtype": "Link", + "options": "Lead", + "width": 130 + }, + { + "label": _("Name"), + "fieldname": "name", + "width": 120 + }, + { + "label": _("Organization"), + "fieldname": "organization", + "width": 120 + }, + { + "label": _("Reference Document Type"), + "fieldname": "reference_document_type", + "fieldtype": "Link", + "options": "Doctype", + "width": 100 + }, + { + "label": _("Reference Name"), + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "options": "reference_document_type", + "width": 140 + }, + { + "label": _("Last Communication"), + "fieldname": "last_communication", + "fieldtype": "Data", + "width": 200 + }, + { + "label": _("Last Communication Date"), + "fieldname": "last_communication_date", + "fieldtype": "Date", + "width": 100 + }] + return columns def get_data(filters): lead_details = [] From ee87484134164cf2360e558fc5249e57252a1eac Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Thu, 11 Feb 2021 20:11:58 +0530 Subject: [PATCH 668/728] fix: calculated discount percentage (#24510) * fix: calculated discount percentage * fix: slider Co-authored-by: Nabin Hait --- erpnext/public/js/controllers/taxes_and_totals.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 22e75780b8..416495ceac 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -4,7 +4,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ setup: function() {}, - apply_pricing_rule_on_item: function(item){ + apply_pricing_rule_on_item: function(item) { let effective_item_rate = item.price_list_rate; let item_rate = item.rate; if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) { @@ -26,6 +26,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ if (item.discount_amount) { item_rate = flt((item.rate_with_margin) - (item.discount_amount), precision('rate', item)); + item.discount_percentage = 100 * flt(item.discount_amount) / flt(item.rate_with_margin); } frappe.model.set_value(item.doctype, item.name, "rate", item_rate); From 7e1dcf911d9552fb84e09158998d1c3c0e26e057 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Thu, 11 Feb 2021 20:19:30 +0530 Subject: [PATCH 669/728] feat: capture Rate of stock UOM in purchase (#24315) * feat: capture Rate of stock UOM in purchase * fix: review changes * added server side code * added stock uom rate in sales transactions * fix: review changes * fix: resolving conflicts * adding patch * fix: removing patch --- .../purchase_invoice_item/purchase_invoice_item.json | 11 ++++++++++- .../sales_invoice_item/sales_invoice_item.json | 11 ++++++++++- .../purchase_order_item/purchase_order_item.json | 11 ++++++++++- erpnext/controllers/stock_controller.py | 6 ++++++ erpnext/public/js/controllers/buying.js | 1 - erpnext/public/js/controllers/transaction.js | 12 ++++++++++-- .../doctype/quotation_item/quotation_item.json | 11 ++++++++++- .../doctype/sales_order_item/sales_order_item.json | 12 ++++++++++-- .../delivery_note_item/delivery_note_item.json | 11 ++++++++++- .../purchase_receipt_item/purchase_receipt_item.json | 11 ++++++++++- 10 files changed, 86 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 1f7853dbf7..07e75acb41 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -40,6 +40,7 @@ "base_rate", "base_amount", "pricing_rules", + "stock_uom_rate", "is_free_item", "section_break_22", "net_rate", @@ -783,6 +784,14 @@ "print_hide": 1, "read_only": 1 }, + { + "depends_on": "eval: doc.uom != doc.stock_uom", + "fieldname": "stock_uom_rate", + "fieldtype": "Currency", + "label": "Rate of Stock UOM", + "options": "currency", + "read_only": 1 + }, { "fieldname": "sales_invoice_item", "fieldtype": "Data", @@ -795,7 +804,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-26 17:20:36.415791", + "modified": "2021-01-30 21:43:21.488258", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 7a98afff36..b403c7b237 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -45,6 +45,7 @@ "base_rate", "base_amount", "pricing_rules", + "stock_uom_rate", "is_free_item", "section_break_21", "net_rate", @@ -811,12 +812,20 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "depends_on": "eval: doc.uom != doc.stock_uom", + "fieldname": "stock_uom_rate", + "fieldtype": "Currency", + "label": "Rate of Stock UOM", + "options": "currency", + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-26 17:25:04.090630", + "modified": "2021-01-30 21:42:37.796771", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", 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 c691e9f9f8..75b2954ddd 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -40,6 +40,7 @@ "base_rate", "base_amount", "pricing_rules", + "stock_uom_rate", "is_free_item", "section_break_29", "net_rate", @@ -726,13 +727,21 @@ "fieldname": "more_info_section_break", "fieldtype": "Section Break", "label": "More Information" + }, + { + "depends_on": "eval: doc.uom != doc.stock_uom", + "fieldname": "stock_uom_rate", + "fieldtype": "Currency", + "label": "Rate of Stock UOM", + "options": "currency", + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-12-07 11:59:47.670951", + "modified": "2021-01-30 21:44:41.816974", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 2ae9dc7102..70e8d8cc78 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -24,6 +24,7 @@ class StockController(AccountsController): self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() + self.set_rate_of_stock_uom() self.validate_internal_transfer() self.validate_putaway_capacity() @@ -396,6 +397,11 @@ class StockController(AccountsController): if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): d.allow_zero_valuation_rate = 1 + def set_rate_of_stock_uom(self): + if self.doctype in ["Purchase Receipt", "Purchase Invoice", "Purchase Order", "Sales Invoice", "Sales Order", "Delivery Note", "Quotation"]: + for d in self.get("items"): + d.stock_uom_rate = d.rate / d.conversion_factor + def validate_internal_transfer(self): if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \ and self.is_internal_transfer(): diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index a2a723dd77..c96386611b 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -191,7 +191,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ 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/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 1db0f5f29d..e5f9049017 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -40,7 +40,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ cur_frm.cscript.set_gross_profit(item); cur_frm.cscript.calculate_taxes_and_totals(); - + cur_frm.cscript.calculate_stock_uom_rate(frm, cdt, cdn); }); @@ -1122,6 +1122,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }); } + me.calculate_stock_uom_rate(doc, cdt, cdn); }, conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) { @@ -1142,6 +1143,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ frappe.meta.has_field(doc.doctype, "price_list_currency")) { this.apply_price_list(item, true); } + this.calculate_stock_uom_rate(doc, cdt, cdn); } }, @@ -1162,9 +1164,15 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ qty: function(doc, cdt, cdn) { let item = frappe.get_doc(cdt, cdn); this.conversion_factor(doc, cdt, cdn, true); + this.calculate_stock_uom_rate(doc, cdt, cdn); this.apply_pricing_rule(item, true); }, + calculate_stock_uom_rate: function(doc, cdt, cdn) { + let item = frappe.get_doc(cdt, cdn); + item.stock_uom_rate = flt(item.rate)/flt(item.conversion_factor); + refresh_field("stock_uom_rate", item.name, item.parentfield); + }, service_stop_date: function(frm, cdt, cdn) { var child = locals[cdt][cdn]; @@ -1275,7 +1283,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ this.frm.set_currency_labels(["base_rate", "base_net_rate", "base_price_list_rate", "base_amount", "base_net_amount"], company_currency, "items"); - this.frm.set_currency_labels(["rate", "net_rate", "price_list_rate", "amount", "net_amount"], + this.frm.set_currency_labels(["rate", "net_rate", "price_list_rate", "amount", "net_amount", "stock_uom_rate"], this.frm.doc.currency, "items"); if(this.frm.fields_dict["operations"]) { diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json index 59ae7b2323..a6785f709a 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.json +++ b/erpnext/selling/doctype/quotation_item/quotation_item.json @@ -47,6 +47,7 @@ "base_amount", "base_net_amount", "pricing_rules", + "stock_uom_rate", "is_free_item", "section_break_43", "valuation_rate", @@ -634,12 +635,20 @@ "print_hide": 1, "read_only": 1, "report_hide": 1 + }, + { + "depends_on": "eval: doc.uom != doc.stock_uom", + "fieldname": "stock_uom_rate", + "fieldtype": "Currency", + "label": "Rate of Stock UOM", + "options": "currency", + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-05-19 20:48:43.222229", + "modified": "2021-01-30 21:39:40.174551", "modified_by": "Administrator", "module": "Selling", "name": "Quotation Item", diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 159655b74b..37e47a9d41 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -46,6 +46,7 @@ "base_rate", "base_amount", "pricing_rules", + "stock_uom_rate", "is_free_item", "section_break_24", "net_rate", @@ -214,7 +215,6 @@ "fieldtype": "Link", "label": "UOM", "options": "UOM", - "print_hide": 0, "reqd": 1 }, { @@ -780,12 +780,20 @@ "fieldname": "manufacturing_section_section", "fieldtype": "Section Break", "label": "Manufacturing Section" + }, + { + "depends_on": "eval: doc.uom != doc.stock_uom", + "fieldname": "stock_uom_rate", + "fieldtype": "Currency", + "label": "Rate of Stock UOM", + "options": "currency", + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-012-07 20:54:32.309460", + "modified": "2021-01-30 21:35:07.617320", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 9de088df0e..17996247c5 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -47,6 +47,7 @@ "base_rate", "base_amount", "pricing_rules", + "stock_uom_rate", "is_free_item", "section_break_25", "net_rate", @@ -743,13 +744,21 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "depends_on": "eval: doc.uom != doc.stock_uom", + "fieldname": "stock_uom_rate", + "fieldtype": "Currency", + "label": "Rate of Stock UOM", + "options": "currency", + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-12-26 17:31:27.029803", + "modified": "2021-01-30 21:42:03.767968", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index e99119202e..8974ad9318 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -48,6 +48,7 @@ "base_rate", "base_amount", "pricing_rules", + "stock_uom_rate", "is_free_item", "section_break_29", "net_rate", @@ -874,6 +875,14 @@ "label": "Received Qty in Stock UOM", "print_hide": 1 }, + { + "depends_on": "eval: doc.uom != doc.stock_uom", + "fieldname": "stock_uom_rate", + "fieldtype": "Currency", + "label": "Rate of Stock UOM", + "options": "currency", + "read_only": 1 + }, { "fieldname": "delivery_note_item", "fieldtype": "Data", @@ -886,7 +895,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-26 16:50:56.479347", + "modified": "2021-01-30 21:44:06.918515", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From bb0d8f8038b1b1b73fdf6e711a53d7eb699950e0 Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Thu, 11 Feb 2021 20:59:28 +0530 Subject: [PATCH 670/728] Update accounts_controller.py --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 19c97b68d3..12a81c7887 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1319,7 +1319,7 @@ def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child for field in ("item_code", "item_name", "description", "item_group"): child_item.update({field: item.get(field)}) date_fieldname = "delivery_date" if child_doctype == "Sales Order Item" else "schedule_date" - child_item.update({date_fieldname: trans_item.get(date_fieldname) or p_doc.date_fieldname}) + child_item.update({date_fieldname: trans_item.get(date_fieldname) or p_doc.get(date_fieldname)}) child_item.uom = trans_item.get("uom") or item.stock_uom conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor From ad1f2b41f47bc0016d4abe7915f0f88af6cbc197 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 12 Feb 2021 11:29:24 +0530 Subject: [PATCH 671/728] fix: Issues with packing items --- erpnext/public/js/utils/serial_no_batch_selector.js | 9 ++++++--- erpnext/selling/doctype/sales_order/sales_order.js | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 2623c3c1a7..d49a8138fb 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -140,6 +140,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ () => me.update_batch_serial_no_items(), () => { refresh_field("items"); + refresh_field("packed_items"); if (me.callback) { return me.callback(me.item); } @@ -154,7 +155,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ if (this.item.serial_no) { this.dialog.fields_dict.serial_no.set_value(this.item.serial_no); } - + if (this.has_batch && !this.has_serial_no && d.batch_no) { this.frm.doc.items.forEach(data => { if(data.item_code == d.item_code) { @@ -231,7 +232,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ this.map_row_values(row, batch, 'batch_no', 'selected_qty', this.values.warehouse); }); - } + } }, update_serial_no_item() { @@ -250,7 +251,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ filters: { 'name': ["in", selected_serial_nos]}, fields: ["batch_no", "name"] }).then((data) => { - // data = [{batch_no: 'batch-1', name: "SR-001"}, + // data = [{batch_no: 'batch-1', name: "SR-001"}, // {batch_no: 'batch-2', name: "SR-003"}, {batch_no: 'batch-2', name: "SR-004"}] const batch_serial_map = data.reduce((acc, d) => { if (!acc[d['batch_no']]) acc[d['batch_no']] = []; @@ -298,6 +299,8 @@ erpnext.SerialNoBatchSelector = Class.extend({ } else { row.warehouse = values.warehouse || warehouse; } + + this.frm.dirty(); }, update_total_qty: function() { diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index e492377385..e3b41e66fb 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -514,7 +514,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( make_delivery_note: function() { frappe.model.open_mapped_doc({ method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note", - frm: me.frm + frm: this.frm }) }, From c44d6b95464c6262cd3082441ea8432194dff8c4 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Fri, 12 Feb 2021 12:24:41 +0530 Subject: [PATCH 672/728] fix: NoneType object has no attribute len() (#24616) --- erpnext/regional/india/e_invoice/utils.py | 64 +++++++++++------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index e016c81409..2043f49825 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -23,7 +23,7 @@ def validate_einvoice_fields(doc): invalid_doctype = doc.doctype != 'Sales Invoice' invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export'] company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin') - no_taxes_applied = len(doc.get('taxes', [])) == 0 + no_taxes_applied = not doc.get('taxes') if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction or no_taxes_applied: return @@ -63,7 +63,7 @@ def get_transaction_details(invoice): elif invoice.gst_category == 'Overseas': supply_type = 'EXPWOP' elif invoice.gst_category == 'Deemed Export': supply_type = 'DEXP' - if not supply_type: + if not supply_type: rr, sez, overseas, export = bold('Registered Regular'), bold('SEZ'), bold('Overseas'), bold('Deemed Export') frappe.throw(_('GST category should be one of {}, {}, {}, {}').format(rr, sez, overseas, export), title=_('Invalid Supply Type')) @@ -128,7 +128,7 @@ def get_gstin_details(gstin): if details: frappe.local.gstin_cache[key] = details return details - + if not details: return GSPConnector.get_gstin_details(gstin) @@ -174,7 +174,7 @@ def get_item_list(invoice): item.serial_no = "" item = update_item_taxes(invoice, item) - + item.total_value = abs( item.taxable_value + item.igst_amount + item.sgst_amount + item.cgst_amount + item.cess_amount + item.cess_nadv_amount + item.other_charges @@ -232,9 +232,9 @@ def get_invoice_value_details(invoice): invoice_value_details.round_off = invoice.base_rounding_adjustment invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(invoice.base_grand_total) invoice_value_details.grand_total = abs(invoice.rounded_total) or abs(invoice.grand_total) - + invoice_value_details = update_invoice_taxes(invoice, invoice_value_details) - + return invoice_value_details def update_invoice_taxes(invoice, invoice_value_details): @@ -251,13 +251,13 @@ def update_invoice_taxes(invoice, invoice_value_details): if t.account_head in gst_accounts.cess_account: # using after discount amt since item also uses after discount amt for cess calc invoice_value_details.total_cess_amt += abs(t.base_tax_amount_after_discount_amount) - + for tax_type in ['igst', 'cgst', 'sgst']: if t.account_head in gst_accounts[f'{tax_type}_account']: invoice_value_details[f'total_{tax_type}_amt'] += abs(t.base_tax_amount_after_discount_amount) else: invoice_value_details.total_other_charges += abs(t.base_tax_amount_after_discount_amount) - + return invoice_value_details def get_payment_details(invoice): @@ -329,23 +329,23 @@ def make_einvoice(invoice): place_of_supply = get_place_of_supply(invoice, invoice.doctype) or invoice.billing_address_gstin place_of_supply = place_of_supply[:2] buyer_details.update(dict(place_of_supply=place_of_supply)) - + shipping_details = payment_details = prev_doc_details = eway_bill_details = frappe._dict({}) if invoice.shipping_address_name and invoice.customer_address != invoice.shipping_address_name: if invoice.gst_category == 'Overseas': shipping_details = get_overseas_address_details(invoice.shipping_address_name) else: shipping_details = get_party_details(invoice.shipping_address_name) - + if invoice.is_pos and invoice.base_paid_amount: payment_details = get_payment_details(invoice) - + if invoice.is_return and invoice.return_against: prev_doc_details = get_return_doc_reference(invoice) - + if invoice.transporter: eway_bill_details = get_eway_bill_details(invoice) - + # not yet implemented dispatch_details = period_details = export_details = frappe._dict({}) @@ -357,7 +357,7 @@ def make_einvoice(invoice): export_details=export_details, eway_bill_details=eway_bill_details ) einvoice = json.loads(einvoice) - + validations = json.loads(read_json('einv_validation')) errors = validate_einvoice(validations, einvoice) if errors: @@ -419,7 +419,7 @@ def validate_einvoice(validations, einvoice, errors=[]): errors.append(_('{} {} should be between {} and {}').format(label, value, minimum, maximum)) if pattern_str and not pattern.match(value): errors.append(field_validation.get('validationMsg')) - + return errors class RequestFailed(Exception): pass @@ -452,19 +452,19 @@ class GSPConnector(): else: credentials = self.e_invoice_settings.credentials[0] if self.e_invoice_settings.credentials else None return credentials - + def get_seller_gstin(self): gstin = self.invoice.company_gstin or frappe.db.get_value('Address', self.invoice.company_address, 'gstin') if not gstin: frappe.throw(_('Cannot retrieve Company GSTIN. Please select company address with valid GSTIN.')) return gstin - + def get_auth_token(self): if time_diff_in_seconds(self.e_invoice_settings.token_expiry, now_datetime()) < 150.0: self.fetch_auth_token() - + return self.e_invoice_settings.auth_token - + def make_request(self, request_type, url, headers=None, data=None): if request_type == 'post': res = make_post_request(url, headers=headers, data=data) @@ -473,7 +473,7 @@ class GSPConnector(): self.log_request(url, headers, data, res) return res - + def log_request(self, url, headers, data, res): headers.update({ 'password': self.credentials.password }) request_log = frappe.get_doc({ @@ -504,7 +504,7 @@ class GSPConnector(): except Exception: self.log_error(res) self.raise_error(True) - + def get_headers(self): return { 'content-type': 'application/json', @@ -526,7 +526,7 @@ class GSPConnector(): else: self.log_error(res) raise RequestFailed - + except RequestFailed: self.raise_error() @@ -571,7 +571,7 @@ class GSPConnector(): else: raise RequestFailed - + except RequestFailed: errors = self.sanitize_error_message(res.get('message')) self.raise_error(errors=errors) @@ -579,7 +579,7 @@ class GSPConnector(): except Exception: self.log_error(data) self.raise_error(True) - + def get_irn_details(self, irn): headers = self.get_headers() @@ -590,7 +590,7 @@ class GSPConnector(): return res.get('result') else: raise RequestFailed - + except RequestFailed: errors = self.sanitize_error_message(res.get('message')) self.raise_error(errors=errors) @@ -598,7 +598,7 @@ class GSPConnector(): except Exception: self.log_error() self.raise_error(True) - + def cancel_irn(self, irn, reason, remark): headers = self.get_headers() data = json.dumps({ @@ -620,7 +620,7 @@ class GSPConnector(): else: raise RequestFailed - + except RequestFailed: errors = self.sanitize_error_message(res.get('message')) self.raise_error(errors=errors) @@ -669,7 +669,7 @@ class GSPConnector(): except Exception: self.log_error(data) self.raise_error(True) - + def cancel_eway_bill(self, eway_bill, reason, remark): headers = self.get_headers() data = json.dumps({ @@ -701,7 +701,7 @@ class GSPConnector(): except Exception: self.log_error(data) self.raise_error(True) - + def sanitize_error_message(self, message): ''' On validation errors, response message looks something like this: @@ -740,7 +740,7 @@ class GSPConnector(): "Exception:", err_tb ]) frappe.log_error(title=_('E Invoice Request Failed'), message=message) - + def raise_error(self, raise_exception=False, errors=[]): title = _('E Invoice Request Failed') if errors: @@ -753,7 +753,7 @@ class GSPConnector(): raise_exception=raise_exception, indicator='red' ) - + def set_einvoice_data(self, res): enc_signed_invoice = res.get('SignedInvoice') dec_signed_invoice = jwt.decode(enc_signed_invoice, verify=False)['data'] @@ -792,7 +792,7 @@ class GSPConnector(): _file.save() frappe.db.commit() self.invoice.qrcode_image = _file.file_url - + def update_invoice(self): self.invoice.flags.ignore_validate_update_after_submit = True self.invoice.flags.ignore_validate = True From 164ffac4efe8d7a71828a513393b8435eb6babf1 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 11 Feb 2021 11:07:59 +0530 Subject: [PATCH 673/728] fix: Consolidated Financial Statement report not works if child company account not present in parent company (#24580) Fixed conflicts --- .../consolidated_financial_statement.py | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index d0116890b6..76f3c50578 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -222,7 +222,7 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i set_gl_entries_by_account(start_date, end_date, root.lft, root.rgt, filters, - gl_entries_by_account, accounts_by_name, ignore_closing_entries=False) + gl_entries_by_account, accounts_by_name, accounts, ignore_closing_entries=False) calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters) accumulate_values_into_parents(accounts, accounts_by_name, companies) @@ -339,7 +339,7 @@ def prepare_data(accounts, start_date, end_date, balance_must_be, companies, com return data def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account, - accounts_by_name, ignore_closing_entries=False): + accounts_by_name, accounts, ignore_closing_entries=False): """Returns a dict like { "account": [gl entries], ... }""" company_lft, company_rgt = frappe.get_cached_value('Company', @@ -382,15 +382,31 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g for entry in gl_entries: key = entry.account_number or entry.account_name - validate_entries(key, entry, accounts_by_name) + validate_entries(key, entry, accounts_by_name, accounts) gl_entries_by_account.setdefault(key, []).append(entry) return gl_entries_by_account -def validate_entries(key, entry, accounts_by_name): +def get_account_details(account): + return frappe.get_cached_value('Account', account, ['name', 'report_type', 'root_type', 'company', + 'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1) + +def validate_entries(key, entry, accounts_by_name, accounts): if key not in accounts_by_name: - field = "Account number" if entry.account_number else "Account name" - frappe.throw(_("{0} {1} is not present in the parent company").format(field, key)) + args = get_account_details(entry.account) + + if args.parent_account: + parent_args = get_account_details(args.parent_account) + + args.update({ + 'lft': parent_args.lft + 1, + 'rgt': parent_args.rgt - 1, + 'root_type': parent_args.root_type, + 'report_type': parent_args.report_type + }) + + accounts_by_name.setdefault(key, args) + accounts.append(args) def get_additional_conditions(from_date, ignore_closing_entries, filters): additional_conditions = [] From fd4e7bdbb11793bf9b809edd2097c9386a0ec8d1 Mon Sep 17 00:00:00 2001 From: Richard Case <64409021+casesolved-co-uk@users.noreply.github.com> Date: Mon, 15 Feb 2021 06:28:27 +0000 Subject: [PATCH 674/728] fix: plaid client version to support latest API (#24531) --- .../doctype/plaid_settings/plaid_connector.py | 4 ++-- requirements.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py index 66d0e5f77d..5f990cdd03 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py @@ -20,7 +20,7 @@ class PlaidConnector(): client_id=self.settings.plaid_client_id, secret=self.settings.get_password("plaid_secret"), environment=self.settings.plaid_env, - api_version="2019-05-29" + api_version="2020-09-14" ) def get_access_token(self, public_token): @@ -29,7 +29,7 @@ class PlaidConnector(): response = self.client.Item.public_token.exchange(public_token) access_token = response["access_token"] return access_token - + def get_token_request(self, update_mode=False): country_codes = ["US", "CA", "FR", "IE", "NL", "ES", "GB"] if self.settings.enable_european_access else ["US", "CA"] args = { diff --git a/requirements.txt b/requirements.txt index 4511aa54d8..5a352364b6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ frappe gocardless-pro==1.11.0 googlemaps==3.1.1 pandas>=1.0.5 -plaid-python==6.0.0 +plaid-python>=7.0.0 pycountry==19.8.18 PyGithub==1.44.1 python-stdnum==1.12 @@ -12,4 +12,4 @@ taxjar==1.9.0 tweepy==3.8.0 Unidecode==1.1.1 WooCommerce==2.1.1 -pycryptodome==3.9.8 \ No newline at end of file +pycryptodome==3.9.8 From 9bf05ad927de697b03e644048ba55b96c2017b2f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 15 Feb 2021 13:32:50 +0530 Subject: [PATCH 675/728] feat: Loans Dashboard --- .../loan_disbursements.json | 29 +++++++ .../dashboard_chart/new_loans/new_loans.json | 30 ++++++++ .../top_10_pledged_loan_securities.json | 29 +++++++ .../dashboard_chart_source/__init__.py | 0 .../__init__.py | 0 .../top_10_pledged_loan_securities.js | 14 ++++ .../top_10_pledged_loan_securities.json | 13 ++++ .../top_10_pledged_loan_securities.py | 76 +++++++++++++++++++ erpnext/loan_management/doctype/loan/loan.py | 10 +++ .../loan_dashboard/loan_dashboard.json | 57 ++++++++++++++ .../active_loans/active_loans.json | 23 ++++++ .../active_securities/active_securities.json | 23 ++++++ .../applicants_with_unpaid_shortfall.json | 21 +++++ .../last_interest_accrual.json | 21 +++++ .../new_loan_applications.json | 23 ++++++ .../number_card/new_loans/new_loans.json | 23 ++++++ .../open_loan_applications.json | 23 ++++++ .../total_disbursed/total_disbursed.json | 23 ++++++ .../total_sanctioned_amount.json | 23 ++++++ .../total_shortfall_amount.json | 23 ++++++ .../applicant_wise_loan_security_exposure.py | 4 +- .../loan_interest_report.py | 2 +- .../loan_security_exposure.py | 2 +- .../loan_management/loan_management.json | 9 ++- 24 files changed, 496 insertions(+), 5 deletions(-) create mode 100644 erpnext/loan_management/dashboard_chart/loan_disbursements/loan_disbursements.json create mode 100644 erpnext/loan_management/dashboard_chart/new_loans/new_loans.json create mode 100644 erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json create mode 100644 erpnext/loan_management/dashboard_chart_source/__init__.py create mode 100644 erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/__init__.py create mode 100644 erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js create mode 100644 erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json create mode 100644 erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py create mode 100644 erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json create mode 100644 erpnext/loan_management/number_card/active_loans/active_loans.json create mode 100644 erpnext/loan_management/number_card/active_securities/active_securities.json create mode 100644 erpnext/loan_management/number_card/applicants_with_unpaid_shortfall/applicants_with_unpaid_shortfall.json create mode 100644 erpnext/loan_management/number_card/last_interest_accrual/last_interest_accrual.json create mode 100644 erpnext/loan_management/number_card/new_loan_applications/new_loan_applications.json create mode 100644 erpnext/loan_management/number_card/new_loans/new_loans.json create mode 100644 erpnext/loan_management/number_card/open_loan_applications/open_loan_applications.json create mode 100644 erpnext/loan_management/number_card/total_disbursed/total_disbursed.json create mode 100644 erpnext/loan_management/number_card/total_sanctioned_amount/total_sanctioned_amount.json create mode 100644 erpnext/loan_management/number_card/total_shortfall_amount/total_shortfall_amount.json diff --git a/erpnext/loan_management/dashboard_chart/loan_disbursements/loan_disbursements.json b/erpnext/loan_management/dashboard_chart/loan_disbursements/loan_disbursements.json new file mode 100644 index 0000000000..b8abf210f8 --- /dev/null +++ b/erpnext/loan_management/dashboard_chart/loan_disbursements/loan_disbursements.json @@ -0,0 +1,29 @@ +{ + "based_on": "disbursement_date", + "chart_name": "Loan Disbursements", + "chart_type": "Sum", + "creation": "2021-02-06 18:40:36.148470", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Loan Disbursement", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Disbursement\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_type": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "modified": "2021-02-06 18:40:49.308663", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Disbursements", + "number_of_groups": 0, + "owner": "Administrator", + "source": "", + "time_interval": "Daily", + "timeseries": 1, + "timespan": "Last Month", + "type": "Line", + "use_report_chart": 0, + "value_based_on": "disbursed_amount", + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json b/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json new file mode 100644 index 0000000000..6e196d25a2 --- /dev/null +++ b/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json @@ -0,0 +1,30 @@ +{ + "based_on": "creation", + "chart_name": "New Loans", + "chart_type": "Count", + "creation": "2021-02-06 16:59:27.509170", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Loan", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_type": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "last_synced_on": "2021-02-06 17:11:05.154562", + "modified": "2021-02-06 17:28:40.134346", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "New Loans", + "number_of_groups": 0, + "owner": "Administrator", + "source": "", + "time_interval": "Daily", + "timeseries": 1, + "timespan": "Last Month", + "type": "Bar", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json b/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json new file mode 100644 index 0000000000..d48a6ce083 --- /dev/null +++ b/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json @@ -0,0 +1,29 @@ +{ + "based_on": "", + "chart_name": "Top 10 Pledged Loan Securities", + "chart_type": "Custom", + "creation": "2021-02-06 22:02:46.284479", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "", + "dynamic_filters_json": "[]", + "filters_json": "[]", + "group_by_type": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "modified": "2021-02-07 17:34:25.117983", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Top 10 Pledged Loan Securities", + "number_of_groups": 0, + "owner": "Administrator", + "source": "Top 10 Pledged Loan Securities", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/loan_management/dashboard_chart_source/__init__.py b/erpnext/loan_management/dashboard_chart_source/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/__init__.py b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js new file mode 100644 index 0000000000..cf75cc8e41 --- /dev/null +++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js @@ -0,0 +1,14 @@ +frappe.provide('frappe.dashboards.chart_sources'); + +frappe.dashboards.chart_sources["Top 10 Pledged Loan Securities"] = { + method: "erpnext.loan_management.dashboard_chart_source.top_10_pledged_loan_securities.top_10_pledged_loan_securities.get_data", + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company") + } + ] +}; \ No newline at end of file diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json new file mode 100644 index 0000000000..42c9b1c335 --- /dev/null +++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json @@ -0,0 +1,13 @@ +{ + "creation": "2021-02-06 22:01:01.332628", + "docstatus": 0, + "doctype": "Dashboard Chart Source", + "idx": 0, + "modified": "2021-02-06 22:01:01.332628", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Top 10 Pledged Loan Securities", + "owner": "Administrator", + "source_name": "Top 10 Pledged Loan Securities ", + "timeseries": 0 +} \ No newline at end of file diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py new file mode 100644 index 0000000000..6bb04401be --- /dev/null +++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py @@ -0,0 +1,76 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils.dashboard import cache_source +from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applicant_wise_loan_security_exposure \ + import get_loan_security_details +from six import iteritems + +@frappe.whitelist() +@cache_source +def get_data(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None, + to_date = None, timespan = None, time_interval = None, heatmap_year = None): + if chart_name: + chart = frappe.get_doc('Dashboard Chart', chart_name) + else: + chart = frappe._dict(frappe.parse_json(chart)) + + filters = {} + current_pledges = {} + + if filters: + filters = frappe.parse_json(filters)[0] + + conditions = "" + labels = [] + values = [] + + if filters.get('company'): + conditions = "AND company = %(company)s" + + loan_security_details = get_loan_security_details() + + unpledges = frappe._dict(frappe.db.sql(""" + SELECT u.loan_security, sum(u.qty) as qty + FROM `tabLoan Security Unpledge` up, `tabUnpledge` u + WHERE u.parent = up.name + AND up.status = 'Approved' + {conditions} + GROUP BY u.loan_security + """.format(conditions=conditions), filters, as_list=1)) + + pledges = frappe._dict(frappe.db.sql(""" + SELECT p.loan_security, sum(p.qty) as qty + FROM `tabLoan Security Pledge` lp, `tabPledge`p + WHERE p.parent = lp.name + AND lp.status = 'Pledged' + {conditions} + GROUP BY p.loan_security + """.format(conditions=conditions), filters, as_list=1)) + + for security, qty in iteritems(pledges): + current_pledges.setdefault(security, qty) + current_pledges[security] -= unpledges.get(security, 0.0) + + sorted_pledges = dict(sorted(current_pledges.items(), key=lambda item: item[1], reverse=True)) + + count = 0 + for security, qty in iteritems(sorted_pledges): + values.append(qty * loan_security_details.get(security, {}).get('latest_price', 0)) + labels.append(security) + count +=1 + + ## Just need top 10 securities + if count == 10: + break + + return { + 'labels': labels, + 'datasets': [{ + 'name': 'Top 10 Securities', + 'chartType': 'bar', + 'values': values + }] + } \ No newline at end of file diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index e607d4f3cb..b481d67491 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -348,3 +348,13 @@ def validate_employee_currency_with_company_currency(applicant, 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)) + +@frappe.whitelist() +def get_shortfall_applicants(): + loans = frappe.get_all('Loan Security Shortfall', {'status': 'Pending'}, pluck='loan') + applicants = set(frappe.get_all('Loan', {'name': ('in', loans)}, pluck='name')) + + return { + "value": len(applicants), + "fieldtype": "Int" + } \ No newline at end of file diff --git a/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json b/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json new file mode 100644 index 0000000000..2da6bb7f01 --- /dev/null +++ b/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json @@ -0,0 +1,57 @@ +{ + "cards": [ + { + "card": "Total Disbursed" + }, + { + "card": "Total Sanctioned Amount" + }, + { + "card": "Active Loans" + }, + { + "card": "Open Loan Applications" + }, + { + "card": "New Loans" + }, + { + "card": "New Loan Applications" + }, + { + "card": "Active Securities" + }, + { + "card": "Applicants With Unpaid Shortfall" + }, + { + "card": "Total Shortfall Amount" + } + ], + "charts": [ + { + "chart": "New Loans", + "width": "Half" + }, + { + "chart": "Loan Disbursements", + "width": "Half" + }, + { + "chart": "Top 10 Pledged Loan Securities", + "width": "Half" + } + ], + "creation": "2021-02-06 16:52:43.484752", + "dashboard_name": "Loan Dashboard", + "docstatus": 0, + "doctype": "Dashboard", + "idx": 0, + "is_default": 0, + "is_standard": 1, + "modified": "2021-02-09 08:07:27.293764", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Dashboard", + "owner": "Administrator" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/active_loans/active_loans.json b/erpnext/loan_management/number_card/active_loans/active_loans.json new file mode 100644 index 0000000000..7e0db47288 --- /dev/null +++ b/erpnext/loan_management/number_card/active_loans/active_loans.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "", + "creation": "2021-02-06 17:10:26.132493", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"status\",\"in\",[\"Disbursed\",\"Partially Disbursed\",null],false]]", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Active Loans", + "modified": "2021-02-06 17:29:20.304087", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Active Loans", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/active_securities/active_securities.json b/erpnext/loan_management/number_card/active_securities/active_securities.json new file mode 100644 index 0000000000..298e41061a --- /dev/null +++ b/erpnext/loan_management/number_card/active_securities/active_securities.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "", + "creation": "2021-02-06 19:07:21.344199", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Security", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Security\",\"disabled\",\"=\",0,false]]", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Active Securities", + "modified": "2021-02-06 19:07:26.671516", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Active Securities", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/applicants_with_unpaid_shortfall/applicants_with_unpaid_shortfall.json b/erpnext/loan_management/number_card/applicants_with_unpaid_shortfall/applicants_with_unpaid_shortfall.json new file mode 100644 index 0000000000..3b9eba1553 --- /dev/null +++ b/erpnext/loan_management/number_card/applicants_with_unpaid_shortfall/applicants_with_unpaid_shortfall.json @@ -0,0 +1,21 @@ +{ + "creation": "2021-02-07 18:55:12.632616", + "docstatus": 0, + "doctype": "Number Card", + "filters_json": "null", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Applicants With Unpaid Shortfall", + "method": "erpnext.loan_management.doctype.loan.loan.get_shortfall_applicants", + "modified": "2021-02-07 21:46:27.369795", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Applicants With Unpaid Shortfall", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Custom" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/last_interest_accrual/last_interest_accrual.json b/erpnext/loan_management/number_card/last_interest_accrual/last_interest_accrual.json new file mode 100644 index 0000000000..65c8ce67d2 --- /dev/null +++ b/erpnext/loan_management/number_card/last_interest_accrual/last_interest_accrual.json @@ -0,0 +1,21 @@ +{ + "creation": "2021-02-07 21:57:14.758007", + "docstatus": 0, + "doctype": "Number Card", + "filters_json": "null", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Last Interest Accrual", + "method": "erpnext.loan_management.doctype.loan.loan.get_last_accrual_date", + "modified": "2021-02-07 21:59:47.525197", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Last Interest Accrual", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Custom" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/new_loan_applications/new_loan_applications.json b/erpnext/loan_management/number_card/new_loan_applications/new_loan_applications.json new file mode 100644 index 0000000000..7e655ff35c --- /dev/null +++ b/erpnext/loan_management/number_card/new_loan_applications/new_loan_applications.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "", + "creation": "2021-02-06 17:59:10.051269", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Application", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Application\",\"docstatus\",\"=\",\"1\",false],[\"Loan Application\",\"creation\",\"Timespan\",\"today\",false]]", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "New Loan Applications", + "modified": "2021-02-06 17:59:21.880979", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "New Loan Applications", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/new_loans/new_loans.json b/erpnext/loan_management/number_card/new_loans/new_loans.json new file mode 100644 index 0000000000..424f0f1495 --- /dev/null +++ b/erpnext/loan_management/number_card/new_loans/new_loans.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "", + "creation": "2021-02-06 17:56:34.624031", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"creation\",\"Timespan\",\"today\",false]]", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "New Loans", + "modified": "2021-02-06 17:58:20.209166", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "New Loans", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/open_loan_applications/open_loan_applications.json b/erpnext/loan_management/number_card/open_loan_applications/open_loan_applications.json new file mode 100644 index 0000000000..1d5e84ed7f --- /dev/null +++ b/erpnext/loan_management/number_card/open_loan_applications/open_loan_applications.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "", + "creation": "2021-02-06 17:23:32.509899", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Application", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Application\",\"docstatus\",\"=\",\"1\",false],[\"Loan Application\",\"status\",\"=\",\"Open\",false]]", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Open Loan Applications", + "modified": "2021-02-06 17:29:09.761011", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Open Loan Applications", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/total_disbursed/total_disbursed.json b/erpnext/loan_management/number_card/total_disbursed/total_disbursed.json new file mode 100644 index 0000000000..4a3f8699a0 --- /dev/null +++ b/erpnext/loan_management/number_card/total_disbursed/total_disbursed.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "disbursed_amount", + "creation": "2021-02-06 16:52:19.505462", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Disbursement", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Disbursement\",\"docstatus\",\"=\",\"1\",false]]", + "function": "Sum", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Total Disbursed Amount", + "modified": "2021-02-06 17:29:38.453870", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Total Disbursed", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/total_sanctioned_amount/total_sanctioned_amount.json b/erpnext/loan_management/number_card/total_sanctioned_amount/total_sanctioned_amount.json new file mode 100644 index 0000000000..dfb9d24e92 --- /dev/null +++ b/erpnext/loan_management/number_card/total_sanctioned_amount/total_sanctioned_amount.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "loan_amount", + "creation": "2021-02-06 17:05:04.704162", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"status\",\"=\",\"Sanctioned\",false]]", + "function": "Sum", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Total Sanctioned Amount", + "modified": "2021-02-06 17:29:29.930557", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Total Sanctioned Amount", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/total_shortfall_amount/total_shortfall_amount.json b/erpnext/loan_management/number_card/total_shortfall_amount/total_shortfall_amount.json new file mode 100644 index 0000000000..aa6b093732 --- /dev/null +++ b/erpnext/loan_management/number_card/total_shortfall_amount/total_shortfall_amount.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "shortfall_amount", + "creation": "2021-02-09 08:07:20.096995", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Security Shortfall", + "dynamic_filters_json": "[]", + "filters_json": "[]", + "function": "Sum", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Total Unpaid Shortfall Amount", + "modified": "2021-02-09 08:09:00.355547", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Total Shortfall Amount", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py index ab586bc09c..0ccd149e5f 100644 --- a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py +++ b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py @@ -36,7 +36,7 @@ def get_columns(filters): def get_data(filters): data = [] - loan_security_details = get_loan_security_details(filters) + loan_security_details = get_loan_security_details() pledge_values, total_value_map, applicant_type_map = get_applicant_wise_total_loan_security_qty(filters, loan_security_details) @@ -64,7 +64,7 @@ def get_data(filters): return data -def get_loan_security_details(filters): +def get_loan_security_details(): security_detail_map = {} loan_security_price_map = {} lsp_validity_map = {} diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py index a3e69bbfbf..0f72c3cce7 100644 --- a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py +++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py @@ -171,7 +171,7 @@ def get_loan_wise_pledges(filters): return current_pledges def get_loan_wise_security_value(filters, current_pledges): - loan_security_details = get_loan_security_details(filters) + loan_security_details = get_loan_security_details() loan_wise_security_value = {} for key in current_pledges: diff --git a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py index adc8013c68..fa55ec57cd 100644 --- a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py +++ b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py @@ -35,7 +35,7 @@ def get_columns(filters): def get_data(filters): data = [] - loan_security_details = get_loan_security_details(filters) + loan_security_details = get_loan_security_details() current_pledges, total_portfolio_value = get_company_wise_loan_security_details(filters, loan_security_details) currency = erpnext.get_company_currency(filters.get('company')) diff --git a/erpnext/loan_management/workspace/loan_management/loan_management.json b/erpnext/loan_management/workspace/loan_management/loan_management.json index 2e8b5bf5b3..848b1e761d 100644 --- a/erpnext/loan_management/workspace/loan_management/loan_management.json +++ b/erpnext/loan_management/workspace/loan_management/loan_management.json @@ -10,6 +10,7 @@ "hide_custom": 0, "icon": "loan", "idx": 0, + "is_default": 0, "is_standard": 1, "label": "Loan Management", "links": [ @@ -219,7 +220,7 @@ "type": "Link" } ], - "modified": "2021-01-12 11:27:56.079724", + "modified": "2021-02-06 17:31:17.593447", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Management", @@ -239,6 +240,12 @@ "label": "Loan", "link_to": "Loan", "type": "DocType" + }, + { + "doc_view": "", + "label": "Dashboard", + "link_to": "Loan Dashboard", + "type": "Dashboard" } ] } \ No newline at end of file From e963362dfd6daa6d2577ad400fe8488c983f809d Mon Sep 17 00:00:00 2001 From: Afshan Date: Mon, 15 Feb 2021 15:00:37 +0530 Subject: [PATCH 676/728] fix: check if customer provided item while setting opening stock --- erpnext/stock/doctype/item/item.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index b661570fd7..7b7d2da969 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -177,7 +177,7 @@ class Item(WebsiteGenerator): if not self.valuation_rate and self.standard_rate: self.valuation_rate = self.standard_rate - if not self.valuation_rate: + if not self.valuation_rate and not self.is_customer_provided_item: frappe.throw(_("Valuation Rate is mandatory if Opening Stock entered")) from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry From 57c3d373a7197ce8da94c9337a63cebf5002845a Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 15 Feb 2021 14:16:00 +0530 Subject: [PATCH 677/728] fix(India): Add GST state code for Ladakh --- erpnext/patches.txt | 1 + .../patches/v12_0/add_state_code_for_ladakh.py | 16 ++++++++++++++++ erpnext/regional/india/__init__.py | 4 +++- erpnext/regional/india/gst_state_code_data.json | 5 +++++ 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v12_0/add_state_code_for_ladakh.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 3b7c6ab48e..e1f7250dec 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -752,3 +752,4 @@ erpnext.patches.v13_0.set_company_in_leave_ledger_entry erpnext.patches.v13_0.convert_qi_parameter_to_link_field erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021 +erpnext.patches.v12_0.add_state_code_for_ladakh diff --git a/erpnext/patches/v12_0/add_state_code_for_ladakh.py b/erpnext/patches/v12_0/add_state_code_for_ladakh.py new file mode 100644 index 0000000000..d41101cc46 --- /dev/null +++ b/erpnext/patches/v12_0/add_state_code_for_ladakh.py @@ -0,0 +1,16 @@ +import frappe +from erpnext.regional.india import states + +def execute(): + + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + custom_fields = ['Address-gst_state', 'Tax Category-gst_state'] + + # Update options in gst_state custom fields + for field in custom_fields: + gst_state_field = frappe.get_doc('Custom Field', field) + gst_state_field.options = '\n'.join(states) + gst_state_field.save() diff --git a/erpnext/regional/india/__init__.py b/erpnext/regional/india/__init__.py index d6221a80aa..378b735e07 100644 --- a/erpnext/regional/india/__init__.py +++ b/erpnext/regional/india/__init__.py @@ -20,6 +20,7 @@ states = [ 'Jharkhand', 'Karnataka', 'Kerala', + 'Ladakh', 'Lakshadweep Islands', 'Madhya Pradesh', 'Maharashtra', @@ -59,6 +60,7 @@ state_numbers = { "Jharkhand": "20", "Karnataka": "29", "Kerala": "32", + "Ladakh": "38", "Lakshadweep Islands": "31", "Madhya Pradesh": "23", "Maharashtra": "27", @@ -80,4 +82,4 @@ state_numbers = { "West Bengal": "19", } -number_state_mapping = {v: k for k, v in iteritems(state_numbers)} \ No newline at end of file +number_state_mapping = {v: k for k, v in iteritems(state_numbers)} diff --git a/erpnext/regional/india/gst_state_code_data.json b/erpnext/regional/india/gst_state_code_data.json index ff88e0f9d6..8481c27972 100644 --- a/erpnext/regional/india/gst_state_code_data.json +++ b/erpnext/regional/india/gst_state_code_data.json @@ -168,5 +168,10 @@ "state_number": "37", "state_code": "AD", "state_name": "Andhra Pradesh (New)" + }, + { + "state_number": "38", + "state_code": "LA", + "state_name": "Ladakh" } ] From 535dc4aea2865a76d0564de7322504383d44f61f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 15 Feb 2021 15:54:25 +0530 Subject: [PATCH 678/728] fix: Do not round off per day interest --- .../doctype/loan_interest_accrual/loan_interest_accrual.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 7d7992d40a..7978350adf 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -246,7 +246,5 @@ def get_per_day_interest(principal_amount, rate_of_interest, posting_date=None): if not posting_date: posting_date = getdate() - precision = cint(frappe.db.get_default("currency_precision")) or 2 - - return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100), precision) + return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100)) From b0735732f960cd3235416568c9227938c9c5388e Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 15 Feb 2021 17:38:40 +0530 Subject: [PATCH 679/728] fix(minor): add erpnext-logo.png --- erpnext/public/images/erpnext-log.png | Bin 0 -> 2360 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 erpnext/public/images/erpnext-log.png diff --git a/erpnext/public/images/erpnext-log.png b/erpnext/public/images/erpnext-log.png new file mode 100644 index 0000000000000000000000000000000000000000..3090727d8ff5f95e49d25278b24b10bf13747f4a GIT binary patch literal 2360 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4Yzkn2Hfk$L90|U1(2s1Lwnj--e zWGoJHcVbv~PUa<$!;&U>cv7h@-A}a#}$5~Go$B+ufw>K9Sh9*k5UVLwOJ(!Va zX^Y;A2EGdl@_l>`i7y#^RP^{9(=r-dj@~ty==pE<-Z0VXbCzc-@3S*_R2nik^f53r zC@?ZGI09YN!~(R06KJv!1A~AH1A~GG1B1haQ3b;&94?q`*kP{E&iDFPT7oB2!_&XD z_5~5k&og@$F%HG3-qv`ucCQqJ+>Ok0zwHd6fknm8%iOovDvb93JGL=j-W!Kx!3@l;vG@68ZvEfg_;k**J@1(rmh2d Date: Mon, 15 Feb 2021 17:40:50 +0530 Subject: [PATCH 680/728] fix(typo): erpnext-logo.png --- .../images/{erpnext-log.png => erpnext-logo.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename erpnext/public/images/{erpnext-log.png => erpnext-logo.png} (100%) diff --git a/erpnext/public/images/erpnext-log.png b/erpnext/public/images/erpnext-logo.png similarity index 100% rename from erpnext/public/images/erpnext-log.png rename to erpnext/public/images/erpnext-logo.png From 243661b37b1a79763e71d46aaa80148bbd6578a0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 15 Feb 2021 19:27:49 +0530 Subject: [PATCH 681/728] fix: formatting query args (#24627) --- erpnext/stock/stock_ledger.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 95f8c438b3..8c9c172ebd 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -194,6 +194,8 @@ class update_entries_after(object): self.process_sle(sle) def get_sle_against_current_voucher(self): + self.args['time_format'] = '%H:%i:%s' + return frappe.db.sql(""" select *, timestamp(posting_date, posting_time) as "timestamp" @@ -202,7 +204,7 @@ class update_entries_after(object): where item_code = %(item_code)s and warehouse = %(warehouse)s - and timestamp(posting_date, time_format(posting_time, '%H:%i:%s')) = timestamp(%(posting_date)s, time_format(%(posting_time)s, '%H:%i:%s')) + and timestamp(posting_date, time_format(posting_time, %(time_format)s)) = timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s)) order by creation ASC for update From d60ff83b6fc42cddea27c85099a322764dd8b115 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 16 Feb 2021 09:12:27 +0530 Subject: [PATCH 682/728] fix: added patch to fix incorrect stock qty and account value (#24628) --- erpnext/accounts/utils.py | 13 ++++++--- erpnext/patches.txt | 1 + .../item_reposting_for_incorrect_sl_and_gl.py | 27 +++++++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 67c7fd2d22..80319056dd 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -82,7 +82,7 @@ def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verb error_msg = _("""{0} {1} is not in any active Fiscal Year""").format(label, formatdate(transaction_date)) if company: error_msg = _("""{0} for {1}""").format(error_msg, frappe.bold(company)) - + if verbose==1: frappe.msgprint(error_msg) raise FiscalYearError(error_msg) @@ -895,7 +895,8 @@ def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for if not warehouse_account: warehouse_account = get_warehouse_account_map(company) - future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items) + future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, + for_warehouses, for_items, company) gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date) for voucher_type, voucher_no in future_stock_vouchers: @@ -909,7 +910,7 @@ def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for else: _delete_gl_entries(voucher_type, voucher_no) -def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None): +def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None, company=None): future_stock_vouchers = [] values = [] @@ -922,6 +923,10 @@ def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, f condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses))) values += for_warehouses + if company: + condition += " and company = %s " + values.append(company) + for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no from `tabStock Ledger Entry` sle where @@ -982,7 +987,7 @@ def check_if_stock_and_account_balance_synced(posting_date, company, voucher_typ error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses as on {3}.").format( stock_bal, account_bal, frappe.bold(account), posting_date) error_resolution = _("Please create an adjustment Journal Entry for amount {0} on {1}")\ - .format(frappe.bold(diff), frappe.bold(posting_date)) + .format(frappe.bold(diff), frappe.bold(posting_date)) frappe.msgprint( msg="""{0}

    {1}

    """.format(error_reason, error_resolution), diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e1f7250dec..e5ee551c11 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -753,3 +753,4 @@ erpnext.patches.v13_0.convert_qi_parameter_to_link_field erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021 erpnext.patches.v12_0.add_state_code_for_ladakh +erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py new file mode 100644 index 0000000000..7c4835a813 --- /dev/null +++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py @@ -0,0 +1,27 @@ +import frappe +from frappe import _ +from erpnext.stock.stock_ledger import update_entries_after +from erpnext.accounts.utils import update_gl_entries_after + + +data = frappe.db.sql(''' SELECT name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time + from `tabStock Ledger Entry` where creation > '2020-12-26 12:58:55.903836' and is_cancelled = 0 + order by timestamp(posting_date, posting_time) asc, creation asc''', as_dict=1) + +for index, d in enumerate(data): + update_entries_after({ + "item_code": d.item_code, + "warehouse": d.warehouse, + "posting_date": d.posting_date, + "posting_time": d.posting_time, + "voucher_type": d.voucher_type, + "voucher_no": d.voucher_no, + "sle_id": d.name + }, allow_negative_stock=True) + +frappe.db.auto_commit_on_many_writes = 1 + +for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}): + update_gl_entries_after('2020-12-25', '01:58:55', company=row.name) + +frappe.db.auto_commit_on_many_writes = 0 \ No newline at end of file From 9a40c1b2e1af132b860452c54aa74f83adc24357 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 16 Feb 2021 10:35:10 +0530 Subject: [PATCH 683/728] fix: patch --- .../item_reposting_for_incorrect_sl_and_gl.py | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py index 7c4835a813..eff0ae4694 100644 --- a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py +++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py @@ -4,24 +4,25 @@ from erpnext.stock.stock_ledger import update_entries_after from erpnext.accounts.utils import update_gl_entries_after -data = frappe.db.sql(''' SELECT name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time - from `tabStock Ledger Entry` where creation > '2020-12-26 12:58:55.903836' and is_cancelled = 0 - order by timestamp(posting_date, posting_time) asc, creation asc''', as_dict=1) +def execute(): + data = frappe.db.sql(''' SELECT name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time + from `tabStock Ledger Entry` where creation > '2020-12-26 12:58:55.903836' and is_cancelled = 0 + order by timestamp(posting_date, posting_time) asc, creation asc''', as_dict=1) -for index, d in enumerate(data): - update_entries_after({ - "item_code": d.item_code, - "warehouse": d.warehouse, - "posting_date": d.posting_date, - "posting_time": d.posting_time, - "voucher_type": d.voucher_type, - "voucher_no": d.voucher_no, - "sle_id": d.name - }, allow_negative_stock=True) + for index, d in enumerate(data): + update_entries_after({ + "item_code": d.item_code, + "warehouse": d.warehouse, + "posting_date": d.posting_date, + "posting_time": d.posting_time, + "voucher_type": d.voucher_type, + "voucher_no": d.voucher_no, + "sle_id": d.name + }, allow_negative_stock=True) -frappe.db.auto_commit_on_many_writes = 1 + frappe.db.auto_commit_on_many_writes = 1 -for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}): - update_gl_entries_after('2020-12-25', '01:58:55', company=row.name) + for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}): + update_gl_entries_after('2020-12-25', '01:58:55', company=row.name) -frappe.db.auto_commit_on_many_writes = 0 \ No newline at end of file + frappe.db.auto_commit_on_many_writes = 0 \ No newline at end of file From 7e8113c7e8b77f62d9af334abe3a2a8c683dbe59 Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 16 Feb 2021 11:59:12 +0530 Subject: [PATCH 684/728] fix: broken image style --- erpnext/public/less/hub.less | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less index 8cb7a9c1ce..c08121f704 100644 --- a/erpnext/public/less/hub.less +++ b/erpnext/public/less/hub.less @@ -32,7 +32,13 @@ body[data-route*="marketplace"] { } .hub-image-loading, .hub-image-broken { - .img-background(); + content: " "; + display: block; + position: absolute; + left: 0; + height: 100%; + width: 100%; + background-color: var(--bg-light-gray); display: flex; align-items: center; justify-content: center; From 9b178bcd10689cfea8d7ad0a850a09e6f03630e0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 16 Feb 2021 14:57:00 +0530 Subject: [PATCH 685/728] fix: stock and account balance syncing (#24644) * fix: stock and account balance syncing * fix: stock and account balance syncing * fix: stock and account balance syncing * fix: minor fix --- erpnext/accounts/utils.py | 14 +++++++++----- erpnext/controllers/stock_controller.py | 3 +-- .../clinical_procedure/clinical_procedure.py | 1 + .../clinical_procedure/test_clinical_procedure.py | 5 +++-- .../item_reposting_for_incorrect_sl_and_gl.py | 1 - erpnext/stock/__init__.py | 2 +- erpnext/stock/doctype/batch/test_batch.py | 6 +++--- .../test_landed_cost_voucher.py | 5 ++++- .../repost_item_valuation/repost_item_valuation.py | 2 +- erpnext/stock/doctype/shipment/test_shipment.py | 1 + erpnext/stock/stock_ledger.py | 3 ++- 11 files changed, 26 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 80319056dd..60d1e20fea 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -888,18 +888,22 @@ def get_coa(doctype, parent, is_root, chart=None): def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, warehouse_account=None, company=None): + stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items, company) + repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company, warehouse_account) + + +def repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company=None, warehouse_account=None): def _delete_gl_entries(voucher_type, voucher_no): frappe.db.sql("""delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no)) + if not warehouse_account: warehouse_account = get_warehouse_account_map(company) - future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, - for_warehouses, for_items, company) - gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date) + gle = get_voucherwise_gl_entries(stock_vouchers, posting_date) - for voucher_type, voucher_no in future_stock_vouchers: + for voucher_type, voucher_no in stock_vouchers: existing_gle = gle.get((voucher_type, voucher_no), []) voucher_obj = frappe.get_doc(voucher_type, voucher_no) expected_gle = voucher_obj.get_gl_entries(warehouse_account) @@ -924,7 +928,7 @@ def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, f values += for_warehouses if company: - condition += " and company = %s " + condition += " and company = %s" values.append(company) for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 70e8d8cc78..ea9659ce01 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -488,13 +488,12 @@ class StockController(AccountsController): "voucher_no": self.name, "company": self.company }) - if check_if_future_sle_exists(args): create_repost_item_valuation_entry(args) elif not is_reposting_pending(): check_if_stock_and_account_balance_synced(self.posting_date, self.company, self.doctype, self.name) - + def is_reposting_pending(): return frappe.db.exists("Repost Item Valuation", {'docstatus': 1, 'status': ['in', ['Queued','In Progress']]}) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index c324228467..325c2094fb 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -121,6 +121,7 @@ class ClinicalProcedure(Document): stock_entry.stock_entry_type = 'Material Receipt' stock_entry.to_warehouse = self.warehouse + stock_entry.company = self.company expense_account = get_account(None, 'expense_account', 'Healthcare Settings', self.company) for item in self.items: if item.qty > item.actual_qty: diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py index 4ee5f6bad3..fb72073a07 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- + # -*- coding: utf-8 -*- # Copyright (c) 2017, ESS LLP and Contributors # See license.txt from __future__ import unicode_literals @@ -60,6 +60,7 @@ def create_procedure(procedure_template, patient, practitioner): procedure.practitioner = practitioner procedure.consume_stock = procedure_template.allow_stock_consumption procedure.items = procedure_template.items - procedure.warehouse = frappe.db.get_single_value('Stock Settings', 'default_warehouse') + procedure.company = "_Test Company" + procedure.warehouse = "_Test Warehouse - _TC" procedure.submit() return procedure \ No newline at end of file diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py index eff0ae4694..f60e0d3036 100644 --- a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py +++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py @@ -3,7 +3,6 @@ from frappe import _ from erpnext.stock.stock_ledger import update_entries_after from erpnext.accounts.utils import update_gl_entries_after - def execute(): data = frappe.db.sql(''' SELECT name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time from `tabStock Ledger Entry` where creation > '2020-12-26 12:58:55.903836' and is_cancelled = 0 diff --git a/erpnext/stock/__init__.py b/erpnext/stock/__init__.py index 8d64efe41d..b3ae804b1c 100644 --- a/erpnext/stock/__init__.py +++ b/erpnext/stock/__init__.py @@ -64,7 +64,7 @@ def get_warehouse_account(warehouse, warehouse_account=None): if not account and warehouse.company: account = get_company_default_inventory_account(warehouse.company) - if not account and warehouse.company: + if not account and warehouse.company and not warehouse.is_group: frappe.throw(_("Please set Account in Warehouse {0} or Default Inventory Account in Company {1}") .format(warehouse.name, warehouse.company)) return account diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py index 97f85bafd9..cbd272df4b 100644 --- a/erpnext/stock/doctype/batch/test_batch.py +++ b/erpnext/stock/doctype/batch/test_batch.py @@ -298,9 +298,9 @@ class TestBatch(unittest.TestCase): self.assertEqual(details.get('price_list_rate'), 400) def create_batch(item_code, rate, create_item_price_for_batch): - pi = make_purchase_invoice(company="_Test Company with perpetual inventory", - warehouse= "Stores - TCP1", cost_center = "Main - TCP1", update_stock=1, - expense_account ="_Test Account Cost for Goods Sold - TCP1", item_code=item_code) + pi = make_purchase_invoice(company="_Test Company", + warehouse= "Stores - _TC", cost_center = "Main - _TC", update_stock=1, + expense_account ="_Test Account Cost for Goods Sold - _TC", item_code=item_code) batch = frappe.db.get_value('Batch', {'item': item_code, 'reference_name': pi.name}) diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py index 144101c67d..984ae46c66 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py @@ -148,7 +148,6 @@ class TestLandedCostVoucher(unittest.TestCase): def test_landed_cost_voucher_for_odd_numbers (self): - pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", do_not_save=True) pr.items[0].cost_center = "Main - TCP1" for x in range(2): @@ -208,6 +207,10 @@ class TestLandedCostVoucher(unittest.TestCase): self.assertEqual(pr.items[1].landed_cost_voucher_amount, 100) def test_multi_currency_lcv(self): + from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records, save_new_records + + save_new_records(test_records) + ## Create USD Shipping charges_account usd_shipping = create_account(account_name="Shipping Charges USD", parent_account="Duties and Taxes - TCP1", company="_Test Company with perpetual inventory", diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index ba2c2c6f44..f22c6018d4 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -64,7 +64,7 @@ def repost(doc): message += "
    " + "Traceback:
    " + traceback frappe.db.set_value(doc.doctype, doc.name, 'error_log', message) - notify_error_to_stock_managers(doc) + notify_error_to_stock_managers(doc, message) doc.set_status('Failed') raise finally: diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py index e1fa207a21..9c3e22f023 100644 --- a/erpnext/stock/doctype/shipment/test_shipment.py +++ b/erpnext/stock/doctype/shipment/test_shipment.py @@ -190,6 +190,7 @@ def create_shipment_company(company_name, abbr): company.abbr = abbr company.default_currency = 'EUR' company.country = 'Germany' + company.enable_perpetual_inventory = 0 company.insert() return company diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 8c9c172ebd..21860b6863 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -204,7 +204,8 @@ class update_entries_after(object): where item_code = %(item_code)s and warehouse = %(warehouse)s - and timestamp(posting_date, time_format(posting_time, %(time_format)s)) = timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s)) + and voucher_type = %(voucher_type)s + and voucher_no = %(voucher_no)s order by creation ASC for update From 7166b6740ff9541d66f0e1abd59e16aa340556c7 Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 16 Feb 2021 16:17:17 +0530 Subject: [PATCH 686/728] fix: duplicate css property --- erpnext/public/less/hub.less | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less index c08121f704..29deada8a4 100644 --- a/erpnext/public/less/hub.less +++ b/erpnext/public/less/hub.less @@ -33,7 +33,6 @@ body[data-route*="marketplace"] { .hub-image-loading, .hub-image-broken { content: " "; - display: block; position: absolute; left: 0; height: 100%; From f9d4d9a095b7278753f053120643e86c9f4f782d Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 16 Feb 2021 18:45:39 +0530 Subject: [PATCH 687/728] test(selling): add test for cancelling sales order Add failing test to reproduce bug in cancelling sales order with advance payments Related issue: ISS-20-21-09586 --- .../doctype/sales_order/test_sales_order.py | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index cbfab8204f..52a0174798 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -17,6 +17,18 @@ from erpnext.selling.doctype.product_bundle.test_product_bundle import make_prod from erpnext.stock.doctype.item.test_item import make_item class TestSalesOrder(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.unlink_setting = int(frappe.db.get_value("Accounts Settings", "Accounts Settings", + "unlink_advance_payment_on_cancelation_of_order")) + + @classmethod + def tearDownClass(cls) -> None: + # reset config to previous state + frappe.db.set_value("Accounts Settings", "Accounts Settings", + "unlink_advance_payment_on_cancelation_of_order", cls.unlink_setting) + def tearDown(self): frappe.set_user("Administrator") @@ -1049,6 +1061,38 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(frappe.LinkExistsError, so_doc.cancel) + def test_cancel_sales_order_after_cancel_payment_entry(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry + # make a sales order + so = make_sales_order() + + # disable unlinking of payment entry + frappe.db.set_value("Accounts Settings", "Accounts Settings", + "unlink_advance_payment_on_cancelation_of_order", 0) + + # create a payment entry against sales order + pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Bank - _TC") + pe.reference_no = "1" + pe.reference_date = nowdate() + pe.paid_from_account_currency = so.currency + pe.paid_to_account_currency = so.currency + pe.source_exchange_rate = 1 + pe.target_exchange_rate = 1 + pe.paid_amount = so.grand_total + pe.save(ignore_permissions=True) + pe.submit() + + # Cancel payment entry + po_doc = frappe.get_doc("Payment Entry", pe.name) + po_doc.cancel() + + # Cancel sales order + try: + so_doc = frappe.get_doc('Sales Order', so.name) + so_doc.cancel() + except Exception: + self.fail("Can not cancel sales order with linked cancelled payment entry") + def test_request_for_raw_materials(self): item = make_item("_Test Finished Item", {"is_stock_item": 1, "maintain_stock": 1, @@ -1207,4 +1251,4 @@ def make_sales_order_workflow(): )) workflow.insert(ignore_permissions=True) - return workflow \ No newline at end of file + return workflow From 3f16902b0090c1c967b1809870831525bf059341 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 16 Feb 2021 18:46:32 +0530 Subject: [PATCH 688/728] fix(selling): cancel sales order with cancelled PE Allow cancelling sales order with cancelled payment entry. Ignoring GL entries while cancelling the document is required to cancel it, reverse entries are created by accounts controller. Related issue: ISS-20-21-09586 --- erpnext/selling/doctype/sales_order/sales_order.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 1516dd6d95..e56129170c 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -180,6 +180,7 @@ class SalesOrder(SellingController): update_coupon_code_count(self.coupon_code,'used') def on_cancel(self): + self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') super(SalesOrder, self).on_cancel() # Cannot cancel closed SO From d826bee13ac6298cea90157a6a804e58eb24c1ca Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 18 Feb 2021 14:14:21 +0530 Subject: [PATCH 689/728] fix: update qty in future sle (#24649) * fix: update qty in future sle * fix: validate cancellation due to ongoing reposting * fix: process sle against current timestamp --- .../doctype/work_order/test_work_order.py | 10 +-- erpnext/stock/__init__.py | 2 +- erpnext/stock/doctype/bin/bin.py | 13 +-- .../delivery_note/test_delivery_note.py | 5 +- .../purchase_receipt/test_purchase_receipt.py | 9 +- .../repost_item_valuation.py | 3 + .../stock_ledger_entry/stock_ledger_entry.py | 1 + erpnext/stock/stock_ledger.py | 87 ++++++++++++++++--- 8 files changed, 102 insertions(+), 28 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 06a8e1987d..00e8c5418a 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -94,11 +94,11 @@ class TestWorkOrder(unittest.TestCase): wo_order = make_wo_order_test_record(item="_Test FG Item", qty=2, source_warehouse=warehouse, skip_transfer=1) - bin1_on_submit = get_bin(item, warehouse) + reserved_qty_on_submission = cint(get_bin(item, warehouse).reserved_qty_for_production) # reserved qty for production is updated - self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2, - cint(bin1_on_submit.reserved_qty_for_production)) + self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2, reserved_qty_on_submission) + test_stock_entry.make_stock_entry(item_code="_Test Item", target=warehouse, qty=100, basic_rate=100) @@ -109,9 +109,9 @@ class TestWorkOrder(unittest.TestCase): s.submit() bin1_at_completion = get_bin(item, warehouse) - + self.assertEqual(cint(bin1_at_completion.reserved_qty_for_production), - cint(bin1_on_submit.reserved_qty_for_production) - 1) + reserved_qty_on_submission - 1) def test_production_item(self): wo_order = make_wo_order_test_record(item="_Test FG Item", qty=1, do_not_save=True) diff --git a/erpnext/stock/__init__.py b/erpnext/stock/__init__.py index b3ae804b1c..9e240cc2b3 100644 --- a/erpnext/stock/__init__.py +++ b/erpnext/stock/__init__.py @@ -70,4 +70,4 @@ def get_warehouse_account(warehouse, warehouse_account=None): return account def get_company_default_inventory_account(company): - return frappe.get_cached_value('Company', company, 'default_inventory_account') + return frappe.get_cached_value('Company', company, 'default_inventory_account') diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 1088b4127d..0514bd2394 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -16,8 +16,9 @@ class Bin(Document): def update_stock(self, args, allow_negative_stock=False, via_landed_cost_voucher=False): '''Called from erpnext.stock.utils.update_bin''' self.update_qty(args) + if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation": - from erpnext.stock.stock_ledger import update_entries_after, validate_negative_qty_in_future_sle + from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle if not args.get("posting_date"): args["posting_date"] = nowdate() @@ -34,11 +35,13 @@ class Bin(Document): "posting_time": args.get("posting_time"), "voucher_type": args.get("voucher_type"), "voucher_no": args.get("voucher_no"), - "sle_id": args.name + "sle_id": args.name, + "creation": args.creation }, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher) - # Validate negative qty in future transactions - validate_negative_qty_in_future_sle(args) + # update qty in future ale and Validate negative qty + update_qty_in_future_sle(args, allow_negative_stock) + def update_qty(self, args): # update the stock values (for current quantities) @@ -51,7 +54,7 @@ class Bin(Document): self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty")) self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty")) self.planned_qty = flt(self.planned_qty) + flt(args.get("planned_qty")) - + self.set_projected_qty() self.db_update() diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 559f8be0de..d39b22965e 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -489,7 +489,10 @@ class TestDeliveryNote(unittest.TestCase): def test_closed_delivery_note(self): from erpnext.stock.doctype.delivery_note.delivery_note import update_delivery_note_status - dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1", do_not_submit=True) + make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100) + + dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', + cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1", do_not_submit=True) dn.submit() diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index ca58ab2823..7741ee7f60 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -94,10 +94,15 @@ class TestPurchaseReceipt(unittest.TestCase): frappe.get_doc('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice').delete() def test_purchase_receipt_no_gl_entry(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') - existing_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item", - "warehouse": "_Test Warehouse - _TC"}, "stock_value") + existing_bin_qty, existing_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item", + "warehouse": "_Test Warehouse - _TC"}, ["actual_qty", "stock_value"]) + + if existing_bin_qty < 0: + make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=abs(existing_bin_qty)) pr = make_purchase_receipt() diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index f22c6018d4..8436acbed2 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -46,6 +46,9 @@ class RepostItemValuation(Document): def repost(doc): try: + if not frappe.db.exists("Repost Item Valuation", doc.name): + return + doc.set_status('In Progress') frappe.db.commit() diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 36d09efd1a..b0e7440e6c 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -38,6 +38,7 @@ class StockLedgerEntry(Document): self.block_transactions_against_group_warehouse() self.validate_with_last_transaction_posting_time() + def on_submit(self): self.check_stock_frozen_date() self.actual_amt_check() diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 21860b6863..e4f5725c68 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -23,6 +23,7 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc cancel = sl_entries[0].get("is_cancelled") if cancel: + validate_cancellation(sl_entries) set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no')) for sle in sl_entries: @@ -45,6 +46,20 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc args = sle_doc.as_dict() update_bin(args, allow_negative_stock, via_landed_cost_voucher) +def validate_cancellation(args): + if args[0].get("is_cancelled"): + repost_entry = frappe.db.get_value("Repost Item Valuation", { + 'voucher_type': args[0].voucher_type, + 'voucher_no': args[0].voucher_no, + 'docstatus': 1 + }, ['name', 'status'], as_dict=1) + + if repost_entry: + if repost_entry.status == 'In Progress': + frappe.throw(_("Cannot cancel the transaction. Reposting of item valuation on submission is not completed yet.")) + if repost_entry.status == 'Queued': + frappe.delete_doc("Repost Item Valuation", repost_entry.name) + def set_as_cancel(voucher_type, voucher_no): frappe.db.sql("""update `tabStock Ledger Entry` set is_cancelled=1, @@ -74,7 +89,8 @@ def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negat "item_code": args[i].item_code, "warehouse": args[i].warehouse, "posting_date": args[i].posting_date, - "posting_time": args[i].posting_time + "posting_time": args[i].posting_time, + "creation": args[i].get("creation") }, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher) for item_wh, new_sle in iteritems(obj.new_items): @@ -86,7 +102,7 @@ def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negat def get_args_for_voucher(voucher_type, voucher_no): return frappe.db.get_all("Stock Ledger Entry", filters={"voucher_type": voucher_type, "voucher_no": voucher_no}, - fields=["item_code", "warehouse", "posting_date", "posting_time"], + fields=["item_code", "warehouse", "posting_date", "posting_time", "creation"], order_by="creation asc", group_by="item_code, warehouse" ) @@ -117,7 +133,7 @@ class update_entries_after(object): self.item_code = args.get("item_code") if self.args.sle_id: self.args['name'] = self.args.sle_id - + self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company") self.get_precision() self.valuation_method = get_valuation_method(self.item_code) @@ -155,7 +171,7 @@ class update_entries_after(object): """ self.data.setdefault(args.warehouse, frappe._dict()) warehouse_dict = self.data[args.warehouse] - previous_sle = self.get_sle_before_datetime(args) + previous_sle = self.get_previous_sle_of_current_voucher(args) warehouse_dict.previous_sle = previous_sle for key in ("qty_after_transaction", "valuation_rate", "stock_value"): @@ -167,9 +183,35 @@ class update_entries_after(object): "stock_value_difference": 0.0 }) + def get_previous_sle_of_current_voucher(self, args): + """get stock ledger entries filtered by specific posting datetime conditions""" + + args['time_format'] = '%H:%i:%s' + if not args.get("posting_date"): + args["posting_date"] = "1900-01-01" + if not args.get("posting_time"): + args["posting_time"] = "00:00" + + sle = frappe.db.sql(""" + select *, timestamp(posting_date, posting_time) as "timestamp" + from `tabStock Ledger Entry` + where item_code = %(item_code)s + and warehouse = %(warehouse)s + and is_cancelled = 0 + and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s)) + order by timestamp(posting_date, posting_time) desc, creation desc + limit 1""", args, as_dict=1) + + return sle[0] if sle else frappe._dict() + + def build(self): + from erpnext.controllers.stock_controller import check_if_future_sle_exists + if self.args.get("sle_id"): - self.process_sle_against_current_voucher() + self.process_sle_against_current_timestamp() + if not check_if_future_sle_exists(self.args): + self.update_bin() else: entries_to_fix = self.get_future_entries_to_fix() @@ -182,13 +224,13 @@ class update_entries_after(object): if sle.dependant_sle_voucher_detail_no: entries_to_fix = self.get_dependent_entries_to_fix(entries_to_fix, sle) + + self.update_bin() if self.exceptions: self.raise_exceptions() - self.update_bin() - - def process_sle_against_current_voucher(self): + def process_sle_against_current_timestamp(self): sl_entries = self.get_sle_against_current_voucher() for sle in sl_entries: self.process_sle(sle) @@ -204,8 +246,8 @@ class update_entries_after(object): where item_code = %(item_code)s and warehouse = %(warehouse)s - and voucher_type = %(voucher_type)s - and voucher_no = %(voucher_no)s + and timestamp(posting_date, time_format(posting_time, %(time_format)s)) = timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s)) + order by creation ASC for update @@ -232,7 +274,6 @@ class update_entries_after(object): return entries_to_fix elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse in self.data: return entries_to_fix - self.initialize_previous_data(dependant_sle) args = self.data[dependant_sle.warehouse].previous_sle \ @@ -639,7 +680,6 @@ class update_entries_after(object): # update bin for each warehouse for warehouse, data in iteritems(self.data): bin_doc = get_bin(self.item_code, warehouse) - bin_doc.update({ "valuation_rate": data.valuation_rate, "actual_qty": data.qty_after_transaction, @@ -765,6 +805,25 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, return valuation_rate +def update_qty_in_future_sle(args, allow_negative_stock=None): + frappe.db.sql(""" + update `tabStock Ledger Entry` + set qty_after_transaction = qty_after_transaction + {qty} + where + item_code = %(item_code)s + and warehouse = %(warehouse)s + and voucher_no != %(voucher_no)s + and is_cancelled = 0 + and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s) + or ( + timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s) + and creation > %(creation)s + ) + ) + """.format(qty=args.actual_qty), args) + + validate_negative_qty_in_future_sle(args, allow_negative_stock) + def validate_negative_qty_in_future_sle(args, allow_negative_stock=None): allow_negative_stock = allow_negative_stock \ or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) @@ -793,7 +852,7 @@ def get_future_sle_with_negative_qty(args): and voucher_no != %(voucher_no)s and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s) and is_cancelled = 0 - and qty_after_transaction + {0} < 0 + and qty_after_transaction < 0 order by timestamp(posting_date, posting_time) asc limit 1 - """.format(args.actual_qty), args, as_dict=1) + """, args, as_dict=1) \ No newline at end of file From c697cb8b2d9ff1da753ca42f9c10c98a68b22c4d Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 11 Feb 2021 18:30:10 +0530 Subject: [PATCH 690/728] fix: validation of job card in stock entry --- .../doctype/job_card/job_card.py | 18 +- .../doctype/job_card_item/job_card_item.json | 455 ++++-------------- .../stock/doctype/stock_entry/stock_entry.py | 3 +- .../stock_entry_detail.json | 14 +- 4 files changed, 136 insertions(+), 354 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index ec28eb7795..662a06b1ee 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -267,6 +267,17 @@ class JobCard(Document): fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"], filters = {"docstatus": 1, "work_order": self.work_order, "operation_id": self.operation_id}) + def set_transferred_qty_in_job_card(self, ste_doc): + for row in ste_doc.items: + if not row.job_card_item: continue + + qty = frappe.db.sql(""" SELECT SUM(qty) from `tabStock Entry Detail` sed, `tabStock Entry` se + WHERE sed.job_card_item = %s and se.docstatus = 1 and sed.parent = se.name and + se.purpose = 'Material Transfer for Manufacture' + """, (row.job_card_item))[0][0] + + frappe.db.set_value('Job Card Item', row.job_card_item, 'transferred_qty', flt(qty)) + def set_transferred_qty(self, update_status=False): if not self.items: self.transferred_qty = self.for_quantity if self.docstatus == 1 else 0 @@ -279,7 +290,8 @@ class JobCard(Document): self.transferred_qty = frappe.db.get_value('Stock Entry', { 'job_card': self.name, 'work_order': self.work_order, - 'docstatus': 1 + 'docstatus': 1, + 'purpose': 'Material Transfer for Manufacture' }, 'sum(fg_completed_qty)') or 0 self.db_set("transferred_qty", self.transferred_qty) @@ -420,6 +432,7 @@ def make_stock_entry(source_name, target_doc=None): target.purpose = "Material Transfer for Manufacture" target.from_bom = 1 target.fg_completed_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0) + target.set_transfer_qty() target.calculate_rate_and_amount() target.set_missing_values() target.set_stock_entry_type() @@ -437,9 +450,10 @@ def make_stock_entry(source_name, target_doc=None): "field_map": { "source_warehouse": "s_warehouse", "required_qty": "qty", - "uom": "stock_uom" + "name": "job_card_item" }, "postprocess": update_item, + "condition": lambda doc: doc.required_qty > 0 } }, target_doc, set_missing_values) diff --git a/erpnext/manufacturing/doctype/job_card_item/job_card_item.json b/erpnext/manufacturing/doctype/job_card_item/job_card_item.json index bc9fe108ca..100ef4ca3a 100644 --- a/erpnext/manufacturing/doctype/job_card_item/job_card_item.json +++ b/erpnext/manufacturing/doctype/job_card_item/job_card_item.json @@ -1,363 +1,120 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-07-09 17:20:44.737289", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2018-07-09 17:20:44.737289", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "source_warehouse", + "uom", + "item_group", + "column_break_3", + "stock_uom", + "item_name", + "description", + "qty_section", + "required_qty", + "column_break_9", + "transferred_qty", + "allow_alternative_item" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "source_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Source Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "source_warehouse", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Source Warehouse", + "options": "Warehouse" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "UOM", - "length": 0, - "no_copy": 0, - "options": "UOM", - "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": "uom", + "fieldtype": "Link", + "label": "UOM", + "options": "UOM" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "description", + "fieldtype": "Text", + "label": "Description", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "qty_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "qty_section", + "fieldtype": "Section Break", + "label": "Qty" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "required_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Required Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "required_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Required Qty", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "allow_alternative_item", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Alternative Item", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "allow_alternative_item", + "fieldtype": "Check", + "label": "Allow Alternative Item" + }, + { + "fetch_from": "item_code.item_group", + "fieldname": "item_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "read_only": 1 + }, + { + "fetch_from": "item_code.stock_uom", + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM" + }, + { + "fieldname": "transferred_qty", + "fieldtype": "Float", + "label": "Transferred Qty", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-08-28 15:23:48.099459", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Job Card Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-02-11 13:50:13.804108", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Job Card Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index d77b70ff14..9cdc3cfa55 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -163,7 +163,7 @@ class StockEntry(StockController): if self.purpose not in valid_purposes: frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes))) - if self.job_card and self.purpose != 'Material Transfer for Manufacture': + if self.job_card and self.purpose not in ['Material Transfer for Manufacture', 'Repack']: frappe.throw(_("For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry") .format(self.job_card)) @@ -823,6 +823,7 @@ class StockEntry(StockController): if self.job_card: job_doc = frappe.get_doc('Job Card', self.job_card) job_doc.set_transferred_qty(update_status=True) + job_doc.set_transferred_qty_in_job_card(self) if self.work_order: pro_doc = frappe.get_doc("Work Order", self.work_order) diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 988ae92969..864ff488b2 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -69,7 +69,8 @@ "putaway_rule", "column_break_51", "reference_purchase_receipt", - "quality_inspection" + "quality_inspection", + "job_card_item" ], "fields": [ { @@ -532,13 +533,22 @@ "fieldname": "is_finished_item", "fieldtype": "Check", "label": "Is Finished Item" + }, + { + "fieldname": "job_card_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Job Card Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-12-30 15:00:44.489442", + "modified": "2021-02-11 13:47:50.158754", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", From ef5c714de2b01788dae3e4bae688822f7ee6974f Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Thu, 18 Feb 2021 15:52:29 +0530 Subject: [PATCH 691/728] fix: salary slip attribute error (#24455) Co-authored-by: Rucha Mahabal --- erpnext/payroll/doctype/salary_slip/salary_slip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 2d3bc57900..60aff02b38 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -1103,10 +1103,10 @@ class SalarySlip(TransactionBase): self.calculate_total_for_salary_slip_based_on_timesheet() else: self.total_deduction = 0.0 - if self.earnings: + if hasattr(self, "earnings"): for earning in self.earnings: self.gross_pay += flt(earning.amount, earning.precision("amount")) - if self.deductions: + if hasattr(self, "deductions"): for deduction in self.deductions: self.total_deduction += flt(deduction.amount, deduction.precision("amount")) self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) - flt(self.total_loan_repayment) From e7bf87cc841cf44309754d152408ebe6ae5933c9 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 18 Feb 2021 16:04:35 +0530 Subject: [PATCH 692/728] fix: POS return for Serialized Items (#24292) Co-authored-by: Nabin Hait Co-authored-by: Saqib --- .../doctype/pos_invoice/pos_invoice.py | 20 +++++-- .../doctype/pos_invoice/test_pos_invoice.py | 59 +++++++++++++++++++ .../pos_invoice_item/pos_invoice_item.json | 12 +++- .../pos_invoice_merge_log.py | 21 +++---- .../controllers/sales_and_purchase_return.py | 9 ++- 5 files changed, 101 insertions(+), 20 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 94573f9df3..76e00923c4 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -179,10 +179,18 @@ class POSInvoice(SalesInvoice): if d.get("serial_no"): serial_nos = get_serial_nos(d.serial_no) for sr in serial_nos: - serial_no_exists = frappe.db.exists("POS Invoice Item", { - "parent": self.return_against, - "serial_no": ["like", d.get("serial_no")] - }) + serial_no_exists = frappe.db.sql(""" + SELECT name + FROM `tabPOS Invoice Item` + WHERE + parent = %s + and (serial_no = %s + or serial_no like %s + or serial_no like %s + or serial_no like %s + ) + """, (self.return_against, sr, sr+'\n%', '%\n'+sr, '%\n'+sr+'\n%')) + if not serial_no_exists: bold_return_against = frappe.bold(self.return_against) bold_serial_no = frappe.bold(sr) @@ -190,7 +198,7 @@ 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") @@ -292,7 +300,7 @@ class POSInvoice(SalesInvoice): if not self.get('payments') and not for_validate: update_multi_mode_option(self, profile) - + if self.is_return and not for_validate: add_return_modes(self, profile) diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 57a23af8af..15875afe87 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -198,6 +198,65 @@ class TestPOSInvoice(unittest.TestCase): self.assertEqual(pos_return.get('payments')[0].amount, -500) self.assertEqual(pos_return.get('payments')[1].amount, -500) + def test_pos_return_for_serialized_item(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + + se = make_serialized_item(company='_Test Company', + target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC') + + serial_nos = get_serial_nos(se.get("items")[0].serial_no) + + pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC', + account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC', + expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC', + item=se.get("items")[0].item_code, rate=1000, do_not_save=1) + + pos.get("items")[0].serial_no = serial_nos[0] + pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1}) + + pos.insert() + pos.submit() + + pos_return = make_sales_return(pos.name) + + pos_return.insert() + pos_return.submit() + self.assertEqual(pos_return.get('items')[0].serial_no, serial_nos[0]) + + def test_partial_pos_returns(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + + se = make_serialized_item(company='_Test Company', + target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC') + + serial_nos = get_serial_nos(se.get("items")[0].serial_no) + + pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC', + account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC', + expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC', + item=se.get("items")[0].item_code, qty=2, rate=1000, do_not_save=1) + + pos.get("items")[0].serial_no = serial_nos[0] + "\n" + serial_nos[1] + pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1}) + + pos.insert() + pos.submit() + + pos_return1 = make_sales_return(pos.name) + + # partial return 1 + pos_return1.get('items')[0].qty = -1 + pos_return1.get('items')[0].serial_no = serial_nos[0] + pos_return1.insert() + pos_return1.submit() + + # partial return 2 + pos_return2 = make_sales_return(pos.name) + self.assertEqual(pos_return2.get('items')[0].qty, -1) + self.assertEqual(pos_return2.get('items')[0].serial_no, serial_nos[1]) + def test_pos_change_amount(self): pos = create_pos_invoice(company= "_Test Company", debit_to="Debtors - _TC", income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105, diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json index 2b6e7de118..8b71eb02fd 100644 --- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json +++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json @@ -87,6 +87,7 @@ "edit_references", "sales_order", "so_detail", + "pos_invoice_item", "column_break_74", "delivery_note", "dn_detail", @@ -790,11 +791,20 @@ "fieldtype": "Link", "label": "Project", "options": "Project" + }, + { + "fieldname": "pos_invoice_item", + "fieldtype": "Data", + "ignore_user_permissions": 1, + "label": "POS Invoice Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2020-07-22 13:40:34.418346", + "modified": "2021-01-04 17:34:49.924531", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice Item", diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index c88d67989b..40f77b4088 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -29,7 +29,7 @@ class POSInvoiceMergeLog(Document): for d in self.pos_invoices: status, docstatus, is_return, return_against = frappe.db.get_value( 'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against']) - + bold_pos_invoice = frappe.bold(d.pos_invoice) bold_status = frappe.bold(status) if docstatus != 1: @@ -58,7 +58,7 @@ class POSInvoiceMergeLog(Document): sales_invoice, credit_note = "", "" if sales: sales_invoice = self.process_merging_into_sales_invoice(sales) - + if returns: credit_note = self.process_merging_into_credit_note(returns) @@ -74,7 +74,7 @@ class POSInvoiceMergeLog(Document): def process_merging_into_sales_invoice(self, data): sales_invoice = self.get_new_sales_invoice() - + sales_invoice = self.merge_pos_invoice_into(sales_invoice, data) sales_invoice.is_consolidated = 1 @@ -98,19 +98,19 @@ class POSInvoiceMergeLog(Document): self.consolidated_credit_note = credit_note.name return credit_note.name - + def merge_pos_invoice_into(self, invoice, data): items, payments, taxes = [], [], [] loyalty_amount_sum, loyalty_points_sum = 0, 0 for doc in data: map_doc(doc, invoice, table_map={ "doctype": invoice.doctype }) - + if doc.redeem_loyalty_points: invoice.loyalty_redemption_account = doc.loyalty_redemption_account invoice.loyalty_redemption_cost_center = doc.loyalty_redemption_cost_center loyalty_points_sum += doc.loyalty_points loyalty_amount_sum += doc.loyalty_amount - + for item in doc.get('items'): found = False for i in items: @@ -118,12 +118,13 @@ class POSInvoiceMergeLog(Document): i.uom == item.uom and i.net_rate == item.net_rate): found = True i.qty = i.qty + item.qty + if not found: item.rate = item.net_rate item.price_list_rate = 0 si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"}) items.append(si_item) - + for tax in doc.get('taxes'): found = False for t in taxes: @@ -162,7 +163,7 @@ class POSInvoiceMergeLog(Document): invoice.ignore_pricing_rule = 1 return invoice - + def get_new_sales_invoice(self): sales_invoice = frappe.new_doc('Sales Invoice') sales_invoice.customer = self.customer @@ -194,7 +195,7 @@ def get_all_unconsolidated_invoices(): } pos_invoices = frappe.db.get_all('POS Invoice', filters=filters, fields=["name as pos_invoice", 'posting_date', 'grand_total', 'customer']) - + return pos_invoices def get_invoice_customer_map(pos_invoices): @@ -204,7 +205,7 @@ def get_invoice_customer_map(pos_invoices): customer = invoice.get('customer') pos_invoice_customer_map.setdefault(customer, []) pos_invoice_customer_map[customer].append(invoice) - + return pos_invoice_customer_map def consolidate_pos_invoices(pos_invoices=[], closing_entry={}): diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 0e1829a767..de61b35316 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -204,8 +204,6 @@ def get_already_returned_items(doc): return items def get_returned_qty_map_for_row(row_name, doctype): - if doctype == "POS Invoice": return {} - child_doctype = doctype + " Item" reference_field = "dn_detail" if doctype == "Delivery Note" else frappe.scrub(child_doctype) @@ -354,7 +352,12 @@ def make_return_doc(doctype, source_name, target_doc=None): target_doc.so_detail = source_doc.so_detail target_doc.dn_detail = source_doc.dn_detail target_doc.expense_account = source_doc.expense_account - target_doc.sales_invoice_item = source_doc.name + + if doctype == "Sales Invoice": + target_doc.sales_invoice_item = source_doc.name + else: + target_doc.pos_invoice_item = source_doc.name + target_doc.price_list_rate = 0 if default_warehouse_for_sales_return: target_doc.warehouse = default_warehouse_for_sales_return From d44e60f17c958f3e58196fce6b4c164123db2db5 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Thu, 18 Feb 2021 11:54:37 +0100 Subject: [PATCH 693/728] feat: add Print Language to Lead and Opportunity (#24317) * fix: move "Print Language" to "More Information" * feat: add "Print Language" in "More Information" Co-authored-by: Rucha Mahabal --- erpnext/buying/doctype/supplier/supplier.json | 4 ++-- erpnext/crm/doctype/lead/lead.json | 11 +++++++++-- erpnext/crm/doctype/opportunity/opportunity.json | 9 ++++++++- erpnext/selling/doctype/customer/customer.json | 6 +++--- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 40362b1d40..4cc5753cbd 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -26,7 +26,6 @@ "supplier_group", "supplier_type", "pan", - "language", "allow_purchase_invoice_creation_without_purchase_order", "allow_purchase_invoice_creation_without_purchase_receipt", "disabled", @@ -57,6 +56,7 @@ "website", "supplier_details", "column_break_30", + "language", "is_frozen" ], "fields": [ @@ -384,7 +384,7 @@ "idx": 370, "image_field": "image", "links": [], - "modified": "2020-06-17 23:18:20", + "modified": "2021-01-06 19:51:40.939087", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index 2df1793fdb..1b33fd73ac 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -49,6 +49,7 @@ "phone", "mobile_no", "fax", + "website", "more_info", "type", "market_segment", @@ -56,8 +57,8 @@ "request_type", "column_break3", "company", - "website", "territory", + "language", "unsubscribed", "blog_subscriber", "title" @@ -447,13 +448,19 @@ "fieldtype": "Select", "label": "Address Type", "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nCurrent\nPermanent\nOther" + }, + { + "fieldname": "language", + "fieldtype": "Link", + "label": "Print Language", + "options": "Language" } ], "icon": "fa fa-user", "idx": 5, "image_field": "image", "links": [], - "modified": "2020-10-13 15:24:00.094811", + "modified": "2021-01-06 19:39:58.748978", "modified_by": "Administrator", "module": "CRM", "name": "Lead", diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index eee13f7e79..2e09a76c0f 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -54,6 +54,7 @@ "campaign", "column_break1", "transaction_date", + "language", "amended_from", "lost_reasons" ], @@ -419,12 +420,18 @@ "fieldtype": "Duration", "label": "First Response Time", "read_only": 1 + }, + { + "fieldname": "language", + "fieldtype": "Link", + "label": "Print Language", + "options": "Language" } ], "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2020-08-12 17:34:35.066961", + "modified": "2021-01-06 19:42:46.190051", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 557c7151d9..3c0652eb67 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -34,9 +34,8 @@ "companies", "currency_and_price_list", "default_currency", - "default_price_list", "column_break_14", - "language", + "default_price_list", "address_contacts", "address_html", "website", @@ -59,6 +58,7 @@ "column_break_45", "market_segment", "industry", + "language", "is_frozen", "column_break_38", "loyalty_program", @@ -485,7 +485,7 @@ "idx": 363, "image_field": "image", "links": [], - "modified": "2020-03-17 11:03:42.706907", + "modified": "2021-01-06 19:35:25.418017", "modified_by": "Administrator", "module": "Selling", "name": "Customer", From ccc80927f6aa3b072b7cac6d9d548394de6f19ba Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 18 Feb 2021 16:41:10 +0530 Subject: [PATCH 694/728] feat: Department Wise Appointment Type charges (#24572) * feat: Appointment Type Service Items Co-Authored-By: muhammad * fix: set practitioner service item charges mandatory on item selection Co-Authored-By: muhammad * feat: use charges from appointment type during billing * feat: handle appointment charges priority for invoice automation * test: patient appointment auto invoicing scenarios * fix: sider * fix: minor fixes Co-authored-by: muhammad Co-authored-by: Nabin Hait --- .../appointment_type/appointment_type.js | 78 ++++++++++++ .../appointment_type/appointment_type.json | 24 +++- .../appointment_type/appointment_type.py | 49 +++++++- .../appointment_type_service_item/__init__.py | 0 .../appointment_type_service_item.json | 67 +++++++++++ .../appointment_type_service_item.py | 10 ++ .../healthcare_practitioner.json | 6 +- .../patient_appointment.js | 112 +++++++++--------- .../patient_appointment.json | 26 ++-- .../patient_appointment.py | 67 +++++++---- .../test_patient_appointment.py | 84 ++++++++++++- erpnext/healthcare/utils.py | 85 ++++++++++--- 12 files changed, 485 insertions(+), 123 deletions(-) create mode 100644 erpnext/healthcare/doctype/appointment_type_service_item/__init__.py create mode 100644 erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json create mode 100644 erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.js b/erpnext/healthcare/doctype/appointment_type/appointment_type.js index 15916a5134..861675acea 100644 --- a/erpnext/healthcare/doctype/appointment_type/appointment_type.js +++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.js @@ -2,4 +2,82 @@ // For license information, please see license.txt frappe.ui.form.on('Appointment Type', { + refresh: function(frm) { + frm.set_query('price_list', function() { + return { + filters: {'selling': 1} + }; + }); + + frm.set_query('medical_department', 'items', function(doc) { + let item_list = doc.items.map(({medical_department}) => medical_department); + return { + filters: [ + ['Medical Department', 'name', 'not in', item_list] + ] + }; + }); + + frm.set_query('op_consulting_charge_item', 'items', function() { + return { + filters: { + is_stock_item: 0 + } + }; + }); + + frm.set_query('inpatient_visit_charge_item', 'items', function() { + return { + filters: { + is_stock_item: 0 + } + }; + }); + } }); + +frappe.ui.form.on('Appointment Type Service Item', { + op_consulting_charge_item: function(frm, cdt, cdn) { + let d = locals[cdt][cdn]; + if (frm.doc.price_list && d.op_consulting_charge_item) { + frappe.call({ + 'method': 'frappe.client.get_value', + args: { + 'doctype': 'Item Price', + 'filters': { + 'item_code': d.op_consulting_charge_item, + 'price_list': frm.doc.price_list + }, + 'fieldname': ['price_list_rate'] + }, + callback: function(data) { + if (data.message.price_list_rate) { + frappe.model.set_value(cdt, cdn, 'op_consulting_charge', data.message.price_list_rate); + } + } + }); + } + }, + + inpatient_visit_charge_item: function(frm, cdt, cdn) { + let d = locals[cdt][cdn]; + if (frm.doc.price_list && d.inpatient_visit_charge_item) { + frappe.call({ + 'method': 'frappe.client.get_value', + args: { + 'doctype': 'Item Price', + 'filters': { + 'item_code': d.inpatient_visit_charge_item, + 'price_list': frm.doc.price_list + }, + 'fieldname': ['price_list_rate'] + }, + callback: function (data) { + if (data.message.price_list_rate) { + frappe.model.set_value(cdt, cdn, 'inpatient_visit_charge', data.message.price_list_rate); + } + } + }); + } + } +}); \ No newline at end of file diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.json b/erpnext/healthcare/doctype/appointment_type/appointment_type.json index 58753bb4f0..3872318287 100644 --- a/erpnext/healthcare/doctype/appointment_type/appointment_type.json +++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.json @@ -12,7 +12,10 @@ "appointment_type", "ip", "default_duration", - "color" + "color", + "billing_section", + "price_list", + "items" ], "fields": [ { @@ -52,10 +55,27 @@ "label": "Color", "no_copy": 1, "report_hide": 1 + }, + { + "fieldname": "billing_section", + "fieldtype": "Section Break", + "label": "Billing" + }, + { + "fieldname": "price_list", + "fieldtype": "Link", + "label": "Price List", + "options": "Price List" + }, + { + "fieldname": "items", + "fieldtype": "Table", + "label": "Appointment Type Service Items", + "options": "Appointment Type Service Item" } ], "links": [], - "modified": "2020-02-03 21:06:05.833050", + "modified": "2021-01-22 09:41:05.010524", "modified_by": "Administrator", "module": "Healthcare", "name": "Appointment Type", diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.py b/erpnext/healthcare/doctype/appointment_type/appointment_type.py index 1dacffab35..67a24f31e0 100644 --- a/erpnext/healthcare/doctype/appointment_type/appointment_type.py +++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.py @@ -4,6 +4,53 @@ from __future__ import unicode_literals from frappe.model.document import Document +import frappe class AppointmentType(Document): - pass + def validate(self): + if self.items and self.price_list: + for item in self.items: + existing_op_item_price = frappe.db.exists('Item Price', { + 'item_code': item.op_consulting_charge_item, + 'price_list': self.price_list + }) + + if not existing_op_item_price and item.op_consulting_charge_item and item.op_consulting_charge: + make_item_price(self.price_list, item.op_consulting_charge_item, item.op_consulting_charge) + + existing_ip_item_price = frappe.db.exists('Item Price', { + 'item_code': item.inpatient_visit_charge_item, + 'price_list': self.price_list + }) + + if not existing_ip_item_price and item.inpatient_visit_charge_item and item.inpatient_visit_charge: + make_item_price(self.price_list, item.inpatient_visit_charge_item, item.inpatient_visit_charge) + +@frappe.whitelist() +def get_service_item_based_on_department(appointment_type, department): + item_list = frappe.db.get_value('Appointment Type Service Item', + filters = {'medical_department': department, 'parent': appointment_type}, + fieldname = ['op_consulting_charge_item', + 'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'], + as_dict = 1 + ) + + # if department wise items are not set up + # use the generic items + if not item_list: + item_list = frappe.db.get_value('Appointment Type Service Item', + filters = {'parent': appointment_type}, + fieldname = ['op_consulting_charge_item', + 'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'], + as_dict = 1 + ) + + return item_list + +def make_item_price(price_list, item, item_price): + frappe.get_doc({ + 'doctype': 'Item Price', + 'price_list': price_list, + 'item_code': item, + 'price_list_rate': item_price + }).insert(ignore_permissions=True, ignore_mandatory=True) diff --git a/erpnext/healthcare/doctype/appointment_type_service_item/__init__.py b/erpnext/healthcare/doctype/appointment_type_service_item/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json new file mode 100644 index 0000000000..5ff68cd682 --- /dev/null +++ b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json @@ -0,0 +1,67 @@ +{ + "actions": [], + "creation": "2021-01-22 09:34:53.373105", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "medical_department", + "op_consulting_charge_item", + "op_consulting_charge", + "column_break_4", + "inpatient_visit_charge_item", + "inpatient_visit_charge" + ], + "fields": [ + { + "fieldname": "medical_department", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Medical Department", + "options": "Medical Department" + }, + { + "fieldname": "op_consulting_charge_item", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Out Patient Consulting Charge Item", + "options": "Item" + }, + { + "fieldname": "op_consulting_charge", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Out Patient Consulting Charge" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "inpatient_visit_charge_item", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Inpatient Visit Charge Item", + "options": "Item" + }, + { + "fieldname": "inpatient_visit_charge", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Inpatient Visit Charge Item" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-01-22 09:35:26.503443", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Appointment Type Service Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py new file mode 100644 index 0000000000..b2e0e82bad --- /dev/null +++ b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, 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 AppointmentTypeServiceItem(Document): + pass diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json index cb747f95ef..8162f03f6d 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json @@ -159,6 +159,7 @@ "fieldname": "op_consulting_charge", "fieldtype": "Currency", "label": "Out Patient Consulting Charge", + "mandatory_depends_on": "op_consulting_charge_item", "options": "Currency" }, { @@ -174,7 +175,8 @@ { "fieldname": "inpatient_visit_charge", "fieldtype": "Currency", - "label": "Inpatient Visit Charge" + "label": "Inpatient Visit Charge", + "mandatory_depends_on": "inpatient_visit_charge_item" }, { "depends_on": "eval: !doc.__islocal", @@ -280,7 +282,7 @@ ], "image_field": "image", "links": [], - "modified": "2020-04-06 13:44:24.759623", + "modified": "2021-01-22 10:14:43.187675", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Practitioner", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index 3d5073b13e..0354733dfb 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -24,11 +24,13 @@ frappe.ui.form.on('Patient Appointment', { }); frm.set_query('practitioner', function() { - return { - filters: { - 'department': frm.doc.department - } - }; + if (frm.doc.department) { + return { + filters: { + 'department': frm.doc.department + } + }; + } }); frm.set_query('service_unit', function() { @@ -140,6 +142,20 @@ frappe.ui.form.on('Patient Appointment', { patient: function(frm) { if (frm.doc.patient) { frm.trigger('toggle_payment_fields'); + frappe.call({ + method: 'frappe.client.get', + args: { + doctype: 'Patient', + name: frm.doc.patient + }, + callback: function (data) { + let age = null; + if (data.message.dob) { + age = calculate_age(data.message.dob); + } + frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', age); + } + }); } else { frm.set_value('patient_name', ''); frm.set_value('patient_sex', ''); @@ -148,6 +164,37 @@ frappe.ui.form.on('Patient Appointment', { } }, + practitioner: function(frm) { + if (frm.doc.practitioner ) { + frm.events.set_payment_details(frm); + } + }, + + appointment_type: function(frm) { + if (frm.doc.appointment_type) { + frm.events.set_payment_details(frm); + } + }, + + set_payment_details: function(frm) { + frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing').then(val => { + if (val) { + frappe.call({ + method: 'erpnext.healthcare.utils.get_service_item_and_practitioner_charge', + args: { + doc: frm.doc + }, + callback: function(data) { + if (data.message) { + frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.practitioner_charge); + frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.service_item); + } + } + }); + } + }); + }, + therapy_plan: function(frm) { frm.trigger('set_therapy_type_filter'); }, @@ -190,14 +237,18 @@ frappe.ui.form.on('Patient Appointment', { // show payment fields as non-mandatory frm.toggle_display('mode_of_payment', 0); frm.toggle_display('paid_amount', 0); + frm.toggle_display('billing_item', 0); frm.toggle_reqd('mode_of_payment', 0); frm.toggle_reqd('paid_amount', 0); + frm.toggle_reqd('billing_item', 0); } else { // if automated appointment invoicing is disabled, hide fields frm.toggle_display('mode_of_payment', data.message ? 1 : 0); frm.toggle_display('paid_amount', data.message ? 1 : 0); + frm.toggle_display('billing_item', data.message ? 1 : 0); frm.toggle_reqd('mode_of_payment', data.message ? 1 : 0); frm.toggle_reqd('paid_amount', data.message ? 1 :0); + frm.toggle_reqd('billing_item', data.message ? 1 : 0); } } }); @@ -540,57 +591,6 @@ let update_status = function(frm, status){ ); }; -frappe.ui.form.on('Patient Appointment', 'practitioner', function(frm) { - if (frm.doc.practitioner) { - frappe.call({ - method: 'frappe.client.get', - args: { - doctype: 'Healthcare Practitioner', - name: frm.doc.practitioner - }, - callback: function (data) { - frappe.model.set_value(frm.doctype, frm.docname, 'department', data.message.department); - frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.op_consulting_charge); - frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.op_consulting_charge_item); - } - }); - } -}); - -frappe.ui.form.on('Patient Appointment', 'patient', function(frm) { - if (frm.doc.patient) { - frappe.call({ - method: 'frappe.client.get', - args: { - doctype: 'Patient', - name: frm.doc.patient - }, - callback: function (data) { - let age = null; - if (data.message.dob) { - age = calculate_age(data.message.dob); - } - frappe.model.set_value(frm.doctype,frm.docname, 'patient_age', age); - } - }); - } -}); - -frappe.ui.form.on('Patient Appointment', 'appointment_type', function(frm) { - if (frm.doc.appointment_type) { - frappe.call({ - method: 'frappe.client.get', - args: { - doctype: 'Appointment Type', - name: frm.doc.appointment_type - }, - callback: function(data) { - frappe.model.set_value(frm.doctype,frm.docname, 'duration',data.message.default_duration); - } - }); - } -}); - let calculate_age = function(birth) { let ageMS = Date.parse(Date()) - Date.parse(birth); let age = new Date(); diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index 35600e4809..83c92af36a 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -19,19 +19,19 @@ "inpatient_record", "column_break_1", "company", + "practitioner", + "practitioner_name", + "department", "service_unit", + "section_break_12", + "appointment_type", + "duration", "procedure_template", "get_procedure_from_encounter", "procedure_prescription", "therapy_plan", "therapy_type", "get_prescribed_therapies", - "practitioner", - "practitioner_name", - "department", - "section_break_12", - "appointment_type", - "duration", "column_break_17", "appointment_date", "appointment_time", @@ -79,6 +79,7 @@ "set_only_once": 1 }, { + "fetch_from": "appointment_type.default_duration", "fieldname": "duration", "fieldtype": "Int", "in_filter": 1, @@ -144,7 +145,6 @@ "in_standard_filter": 1, "label": "Healthcare Practitioner", "options": "Healthcare Practitioner", - "read_only": 1, "reqd": 1, "search_index": 1, "set_only_once": 1 @@ -158,7 +158,6 @@ "in_standard_filter": 1, "label": "Department", "options": "Medical Department", - "read_only": 1, "search_index": 1, "set_only_once": 1 }, @@ -227,12 +226,14 @@ "fieldname": "mode_of_payment", "fieldtype": "Link", "label": "Mode of Payment", - "options": "Mode of Payment" + "options": "Mode of Payment", + "read_only_depends_on": "invoiced" }, { "fieldname": "paid_amount", "fieldtype": "Currency", - "label": "Paid Amount" + "label": "Paid Amount", + "read_only_depends_on": "invoiced" }, { "fieldname": "column_break_2", @@ -302,7 +303,8 @@ "fieldname": "therapy_plan", "fieldtype": "Link", "label": "Therapy Plan", - "options": "Therapy Plan" + "options": "Therapy Plan", + "set_only_once": 1 }, { "fieldname": "ref_sales_invoice", @@ -347,7 +349,7 @@ } ], "links": [], - "modified": "2020-12-16 13:16:58.578503", + "modified": "2021-02-08 13:13:15.116833", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index f2b94b8e9c..1f76cd624c 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -26,6 +26,7 @@ class PatientAppointment(Document): def after_insert(self): self.update_prescription_details() + self.set_payment_details() invoice_appointment(self) self.update_fee_validity() send_confirmation_msg(self) @@ -85,6 +86,13 @@ class PatientAppointment(Document): def set_appointment_datetime(self): self.appointment_datetime = "%s %s" % (self.appointment_date, self.appointment_time or "00:00:00") + def set_payment_details(self): + if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'): + details = get_service_item_and_practitioner_charge(self) + self.db_set('billing_item', details.get('service_item')) + if not self.paid_amount: + self.db_set('paid_amount', details.get('practitioner_charge')) + def validate_customer_created(self): if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'): if not frappe.db.get_value('Patient', self.patient, 'customer'): @@ -148,31 +156,37 @@ def invoice_appointment(appointment_doc): fee_validity = None if automate_invoicing and not appointment_invoiced and not fee_validity: - sales_invoice = frappe.new_doc('Sales Invoice') - sales_invoice.patient = appointment_doc.patient - sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer') - sales_invoice.appointment = appointment_doc.name - sales_invoice.due_date = getdate() - sales_invoice.company = appointment_doc.company - sales_invoice.debit_to = get_receivable_account(appointment_doc.company) + create_sales_invoice(appointment_doc) - item = sales_invoice.append('items', {}) - item = get_appointment_item(appointment_doc, item) - # Add payments if payment details are supplied else proceed to create invoice as Unpaid - if appointment_doc.mode_of_payment and appointment_doc.paid_amount: - sales_invoice.is_pos = 1 - payment = sales_invoice.append('payments', {}) - payment.mode_of_payment = appointment_doc.mode_of_payment - payment.amount = appointment_doc.paid_amount +def create_sales_invoice(appointment_doc): + sales_invoice = frappe.new_doc('Sales Invoice') + sales_invoice.patient = appointment_doc.patient + sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer') + sales_invoice.appointment = appointment_doc.name + sales_invoice.due_date = getdate() + sales_invoice.company = appointment_doc.company + sales_invoice.debit_to = get_receivable_account(appointment_doc.company) - sales_invoice.set_missing_values(for_validate=True) - sales_invoice.flags.ignore_mandatory = True - sales_invoice.save(ignore_permissions=True) - sales_invoice.submit() - frappe.msgprint(_('Sales Invoice {0} created').format(sales_invoice.name), alert=True) - frappe.db.set_value('Patient Appointment', appointment_doc.name, 'invoiced', 1) - frappe.db.set_value('Patient Appointment', appointment_doc.name, 'ref_sales_invoice', sales_invoice.name) + item = sales_invoice.append('items', {}) + item = get_appointment_item(appointment_doc, item) + + # Add payments if payment details are supplied else proceed to create invoice as Unpaid + if appointment_doc.mode_of_payment and appointment_doc.paid_amount: + sales_invoice.is_pos = 1 + payment = sales_invoice.append('payments', {}) + payment.mode_of_payment = appointment_doc.mode_of_payment + payment.amount = appointment_doc.paid_amount + + sales_invoice.set_missing_values(for_validate=True) + sales_invoice.flags.ignore_mandatory = True + sales_invoice.save(ignore_permissions=True) + sales_invoice.submit() + frappe.msgprint(_('Sales Invoice {0} created').format(sales_invoice.name), alert=True) + frappe.db.set_value('Patient Appointment', appointment_doc.name, { + 'invoiced': 1, + 'ref_sales_invoice': sales_invoice.name + }) def check_is_new_patient(patient, name=None): @@ -187,13 +201,14 @@ def check_is_new_patient(patient, name=None): def get_appointment_item(appointment_doc, item): - service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment_doc) - item.item_code = service_item + details = get_service_item_and_practitioner_charge(appointment_doc) + charge = appointment_doc.paid_amount or details.get('practitioner_charge') + item.item_code = details.get('service_item') item.description = _('Consulting Charges: {0}').format(appointment_doc.practitioner) item.income_account = get_income_account(appointment_doc.practitioner, appointment_doc.company) item.cost_center = frappe.get_cached_value('Company', appointment_doc.company, 'cost_center') - item.rate = practitioner_charge - item.amount = practitioner_charge + item.rate = charge + item.amount = charge item.qty = 1 item.reference_dt = 'Patient Appointment' item.reference_dn = appointment_doc.name diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py index f7ec6f58fc..2bb8a53c45 100644 --- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py @@ -32,7 +32,8 @@ class TestPatientAppointment(unittest.TestCase): patient, medical_department, practitioner = create_healthcare_docs() frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4), invoice = 1) - self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 1) + appointment.reload() + self.assertEqual(appointment.invoiced, 1) encounter = make_encounter(appointment.name) self.assertTrue(encounter) self.assertEqual(encounter.company, appointment.company) @@ -41,7 +42,7 @@ class TestPatientAppointment(unittest.TestCase): # invoiced flag mapped from appointment self.assertEqual(encounter.invoiced, frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced')) - def test_invoicing(self): + def test_auto_invoicing(self): patient, medical_department, practitioner = create_healthcare_docs() frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0) frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0) @@ -57,6 +58,50 @@ class TestPatientAppointment(unittest.TestCase): self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'patient'), appointment.patient) self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount) + def test_auto_invoicing_based_on_department(self): + patient, medical_department, practitioner = create_healthcare_docs() + frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0) + frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) + appointment_type = create_appointment_type() + + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), + invoice=1, appointment_type=appointment_type.name, department='_Test Medical Department') + appointment.reload() + + self.assertEqual(appointment.invoiced, 1) + self.assertEqual(appointment.billing_item, 'HLC-SI-001') + self.assertEqual(appointment.paid_amount, 200) + + sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent') + self.assertTrue(sales_invoice_name) + self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount) + + def test_auto_invoicing_according_to_appointment_type_charge(self): + patient, medical_department, practitioner = create_healthcare_docs() + frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0) + frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) + + item = create_healthcare_service_items() + items = [{ + 'op_consulting_charge_item': item, + 'op_consulting_charge': 300 + }] + appointment_type = create_appointment_type(args={ + 'name': 'Generic Appointment Type charge', + 'items': items + }) + + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), + invoice=1, appointment_type=appointment_type.name) + appointment.reload() + + self.assertEqual(appointment.invoiced, 1) + self.assertEqual(appointment.billing_item, item) + self.assertEqual(appointment.paid_amount, 300) + + sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent') + self.assertTrue(sales_invoice_name) + def test_appointment_cancel(self): patient, medical_department, practitioner = create_healthcare_docs() frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 1) @@ -178,14 +223,15 @@ def create_encounter(appointment): encounter.submit() return encounter -def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0, service_unit=None, save=1): +def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0, + service_unit=None, appointment_type=None, save=1, department=None): item = create_healthcare_service_items() frappe.db.set_value('Healthcare Settings', None, 'inpatient_visit_charge_item', item) frappe.db.set_value('Healthcare Settings', None, 'op_consulting_charge_item', item) appointment = frappe.new_doc('Patient Appointment') appointment.patient = patient appointment.practitioner = practitioner - appointment.department = '_Test Medical Department' + appointment.department = department or '_Test Medical Department' appointment.appointment_date = appointment_date appointment.company = '_Test Company' appointment.duration = 15 @@ -193,7 +239,8 @@ def create_appointment(patient, practitioner, appointment_date, invoice=0, proce appointment.service_unit = service_unit if invoice: appointment.mode_of_payment = 'Cash' - appointment.paid_amount = 500 + if appointment_type: + appointment.appointment_type = appointment_type if procedure_template: appointment.procedure_template = create_clinical_procedure_template().get('name') if save: @@ -223,4 +270,29 @@ def create_clinical_procedure_template(): template.description = 'Knee Surgery and Rehab' template.rate = 50000 template.save() - return template \ No newline at end of file + return template + +def create_appointment_type(args=None): + if not args: + args = frappe.local.form_dict + + name = args.get('name') or 'Test Appointment Type wise Charge' + + if frappe.db.exists('Appointment Type', name): + return frappe.get_doc('Appointment Type', name) + + else: + item = create_healthcare_service_items() + items = [{ + 'medical_department': '_Test Medical Department', + 'op_consulting_charge_item': item, + 'op_consulting_charge': 200 + }] + return frappe.get_doc({ + 'doctype': 'Appointment Type', + 'appointment_type': args.get('name') or 'Test Appointment Type wise Charge', + 'default_duration': args.get('default_duration') or 20, + 'color': args.get('color') or '#7575ff', + 'price_list': args.get('price_list') or frappe.db.get_value("Price List", {"selling": 1}), + 'items': args.get('items') or items + }).insert() \ No newline at end of file diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index d4027dff4e..d3d22c80b6 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -5,9 +5,11 @@ from __future__ import unicode_literals import math import frappe +import json from frappe import _ from frappe.utils.formatters import format_value from frappe.utils import time_diff_in_hours, rounded +from six import string_types from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple @@ -64,7 +66,9 @@ def get_appointments_to_invoice(patient, company): income_account = None service_item = None if appointment.practitioner: - service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment) + details = get_service_item_and_practitioner_charge(appointment) + service_item = details.get('service_item') + practitioner_charge = details.get('practitioner_charge') income_account = get_income_account(appointment.practitioner, appointment.company) appointments_to_invoice.append({ 'reference_type': 'Patient Appointment', @@ -97,7 +101,9 @@ def get_encounters_to_invoice(patient, company): frappe.db.get_single_value('Healthcare Settings', 'do_not_bill_inpatient_encounters'): continue - service_item, practitioner_charge = get_service_item_and_practitioner_charge(encounter) + details = get_service_item_and_practitioner_charge(encounter) + service_item = details.get('service_item') + practitioner_charge = details.get('practitioner_charge') income_account = get_income_account(encounter.practitioner, encounter.company) encounters_to_invoice.append({ @@ -173,7 +179,7 @@ def get_clinical_procedures_to_invoice(patient, company): if procedure.invoice_separately_as_consumables and procedure.consume_stock \ and procedure.status == 'Completed' and not procedure.consumption_invoiced: - service_item = get_healthcare_service_item('clinical_procedure_consumable_item') + service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item') if not service_item: msg = _('Please Configure Clinical Procedure Consumable Item in ') msg += '''Healthcare Settings''' @@ -304,24 +310,50 @@ def get_therapy_sessions_to_invoice(patient, company): return therapy_sessions_to_invoice - +@frappe.whitelist() def get_service_item_and_practitioner_charge(doc): + if isinstance(doc, string_types): + doc = json.loads(doc) + doc = frappe.get_doc(doc) + + service_item = None + practitioner_charge = None + department = doc.medical_department if doc.doctype == 'Patient Encounter' else doc.department + is_inpatient = doc.inpatient_record - if is_inpatient: - service_item = get_practitioner_service_item(doc.practitioner, 'inpatient_visit_charge_item') + + if doc.get('appointment_type'): + service_item, practitioner_charge = get_appointment_type_service_item(doc.appointment_type, department, is_inpatient) + + if not service_item and not practitioner_charge: + service_item, practitioner_charge = get_practitioner_service_item(doc.practitioner, is_inpatient) if not service_item: - service_item = get_healthcare_service_item('inpatient_visit_charge_item') - else: - service_item = get_practitioner_service_item(doc.practitioner, 'op_consulting_charge_item') - if not service_item: - service_item = get_healthcare_service_item('op_consulting_charge_item') + service_item = get_healthcare_service_item(is_inpatient) + if not service_item: throw_config_service_item(is_inpatient) - practitioner_charge = get_practitioner_charge(doc.practitioner, is_inpatient) if not practitioner_charge: throw_config_practitioner_charge(is_inpatient, doc.practitioner) + return {'service_item': service_item, 'practitioner_charge': practitioner_charge} + + +def get_appointment_type_service_item(appointment_type, department, is_inpatient): + from erpnext.healthcare.doctype.appointment_type.appointment_type import get_service_item_based_on_department + + item_list = get_service_item_based_on_department(appointment_type, department) + service_item = None + practitioner_charge = None + + if item_list: + if is_inpatient: + service_item = item_list.get('inpatient_visit_charge_item') + practitioner_charge = item_list.get('inpatient_visit_charge') + else: + service_item = item_list.get('op_consulting_charge_item') + practitioner_charge = item_list.get('op_consulting_charge') + return service_item, practitioner_charge @@ -345,12 +377,27 @@ def throw_config_practitioner_charge(is_inpatient, practitioner): frappe.throw(msg, title=_('Missing Configuration')) -def get_practitioner_service_item(practitioner, service_item_field): - return frappe.db.get_value('Healthcare Practitioner', practitioner, service_item_field) +def get_practitioner_service_item(practitioner, is_inpatient): + service_item = None + practitioner_charge = None + + if is_inpatient: + service_item, practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, ['inpatient_visit_charge_item', 'inpatient_visit_charge']) + else: + service_item, practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, ['op_consulting_charge_item', 'op_consulting_charge']) + + return service_item, practitioner_charge -def get_healthcare_service_item(service_item_field): - return frappe.db.get_single_value('Healthcare Settings', service_item_field) +def get_healthcare_service_item(is_inpatient): + service_item = None + + if is_inpatient: + service_item = frappe.db.get_single_value('Healthcare Settings', 'inpatient_visit_charge_item') + else: + service_item = frappe.db.get_single_value('Healthcare Settings', 'op_consulting_charge_item') + + return service_item def get_practitioner_charge(practitioner, is_inpatient): @@ -381,7 +428,8 @@ def set_invoiced(item, method, ref_invoice=None): invoiced = True if item.reference_dt == 'Clinical Procedure': - if get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code: + service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item') + if service_item == item.item_code: frappe.db.set_value(item.reference_dt, item.reference_dn, 'consumption_invoiced', invoiced) else: frappe.db.set_value(item.reference_dt, item.reference_dn, 'invoiced', invoiced) @@ -403,7 +451,8 @@ def set_invoiced(item, method, ref_invoice=None): def validate_invoiced_on_submit(item): - if item.reference_dt == 'Clinical Procedure' and get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code: + if item.reference_dt == 'Clinical Procedure' and \ + frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item') == item.item_code: is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'consumption_invoiced') else: is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'invoiced') From 4858b40813addf9fe10e0c7a65de30660e8abd9d Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 18 Feb 2021 18:44:29 +0530 Subject: [PATCH 695/728] fix: excluding unidentified accounts from gstr-1 --- erpnext/regional/report/gstr_1/gstr_1.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 96dc3f728d..09b04ff367 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -236,6 +236,7 @@ class Gstr1Report(object): self.cgst_sgst_invoices = [] unidentified_gst_accounts = [] + unidentified_gst_accounts_invoice = [] for parent, account, item_wise_tax_detail, tax_amount in self.tax_details: if account in self.gst_accounts.cess_account: self.invoice_cess.setdefault(parent, tax_amount) @@ -251,6 +252,7 @@ class Gstr1Report(object): if not (cgst_or_sgst or account in self.gst_accounts.igst_account): if "gst" in account.lower() and account not in unidentified_gst_accounts: unidentified_gst_accounts.append(account) + unidentified_gst_accounts_invoice.append(parent) continue for item_code, tax_amounts in item_wise_tax_detail.items(): @@ -273,7 +275,7 @@ class Gstr1Report(object): # Build itemised tax for export invoices where tax table is blank for invoice, items in iteritems(self.invoice_items): - if invoice not in self.items_based_on_tax_rate \ + if invoice not in self.items_based_on_tax_rate and invoice not in unidentified_gst_accounts_invoice \ and frappe.db.get_value(self.doctype, invoice, "export_type") == "Without Payment of Tax": self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys()) From bc857522ad376fc85d8f24449f7daedca86badd4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 19 Feb 2021 10:13:56 +0530 Subject: [PATCH 696/728] fix: Loan Interest Accrual Chart --- .../loan_interest_accrual.json | 29 +++++++++++++++++++ .../loan_dashboard/loan_dashboard.json | 6 +++- .../loan_management/loan_management.json | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json diff --git a/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json b/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json new file mode 100644 index 0000000000..5b3c0e3dd3 --- /dev/null +++ b/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json @@ -0,0 +1,29 @@ +{ + "based_on": "posting_date", + "chart_name": "Loan Interest Accrual", + "chart_type": "Sum", + "creation": "2021-02-18 20:07:04.843876", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Loan Interest Accrual", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Interest Accrual\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_type": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "modified": "2021-02-18 20:07:10.015299", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Interest Accrual", + "number_of_groups": 0, + "owner": "Administrator", + "source": "", + "time_interval": "Monthly", + "timeseries": 1, + "timespan": "Last Year", + "type": "Line", + "use_report_chart": 0, + "value_based_on": "interest_amount", + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json b/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json index 2da6bb7f01..bd04cfe4aa 100644 --- a/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json +++ b/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json @@ -40,6 +40,10 @@ { "chart": "Top 10 Pledged Loan Securities", "width": "Half" + }, + { + "chart": "Loan Interest Accrual", + "width": "Half" } ], "creation": "2021-02-06 16:52:43.484752", @@ -49,7 +53,7 @@ "idx": 0, "is_default": 0, "is_standard": 1, - "modified": "2021-02-09 08:07:27.293764", + "modified": "2021-02-18 20:07:10.095401", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Dashboard", diff --git a/erpnext/loan_management/workspace/loan_management/loan_management.json b/erpnext/loan_management/workspace/loan_management/loan_management.json index 848b1e761d..18559dceef 100644 --- a/erpnext/loan_management/workspace/loan_management/loan_management.json +++ b/erpnext/loan_management/workspace/loan_management/loan_management.json @@ -220,7 +220,7 @@ "type": "Link" } ], - "modified": "2021-02-06 17:31:17.593447", + "modified": "2021-02-18 17:31:53.586508", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Management", From 7efbbcf3aa7f36a4ff78e561a8b96abe37ea4ea5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 19 Feb 2021 10:53:58 +0530 Subject: [PATCH 697/728] fix: Do not delete GST Accounts from GST Settings on clearing transactions from Company Master --- erpnext/setup/doctype/company/delete_company_transactions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py index 7a72fe3102..0df4c87f51 100644 --- a/erpnext/setup/doctype/company/delete_company_transactions.py +++ b/erpnext/setup/doctype/company/delete_company_transactions.py @@ -28,7 +28,7 @@ def delete_company_transactions(company_name): "Party Account", "Employee", "Sales Taxes and Charges Template", "Purchase Taxes and Charges Template", "POS Profile", "BOM", "Company", "Bank Account", "Item Tax Template", "Mode Of Payment", - "Item Default", "Customer", "Supplier"): + "Item Default", "Customer", "Supplier", "GST Account"): delete_for_doctype(doctype, company_name) # reset company values From 6a5ef261f20a68d6ab0acba5e614d9506da77cb5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 19 Feb 2021 14:30:23 +0530 Subject: [PATCH 698/728] feat: Normal rounding for GST Taxes (#24488) * feat: Normal rounding for GST Taxes * fix: Add test case Co-authored-by: Nabin Hait --- erpnext/__init__.py | 2 +- erpnext/controllers/taxes_and_totals.py | 19 ++ erpnext/hooks.py | 1 + .../public/js/controllers/taxes_and_totals.js | 30 +- .../doctype/gst_settings/gst_settings.json | 290 +++++------------- .../gstr_3b_report/test_gstr_3b_report.py | 67 +++- erpnext/regional/india/utils.py | 21 ++ 7 files changed, 198 insertions(+), 232 deletions(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 5a5c448026..199a183e47 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -109,7 +109,7 @@ def get_region(company=None): ''' if company or frappe.flags.company: return frappe.get_cached_value('Company', - company or frappe.flags.company, 'country') + company or frappe.flags.company, 'country') elif frappe.flags.country: return frappe.flags.country else: diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index cfa499191c..10271cbcc9 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -15,6 +15,8 @@ from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_ra class calculate_taxes_and_totals(object): def __init__(self, doc): self.doc = doc + frappe.flags.round_off_applicable_accounts = [] + get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts) self.calculate() def calculate(self): @@ -332,10 +334,18 @@ class calculate_taxes_and_totals(object): elif tax.charge_type == "On Item Quantity": current_tax_amount = tax_rate * item.qty + current_tax_amount = self.get_final_current_tax_amount(tax, current_tax_amount) self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount) return current_tax_amount + def get_final_current_tax_amount(self, tax, current_tax_amount): + # Some countries need individual tax components to be rounded + # Handeled via regional doctypess + if tax.account_head in frappe.flags.round_off_applicable_accounts: + current_tax_amount = round(current_tax_amount, 0) + return current_tax_amount + def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount): # store tax breakup for each item key = item.item_code or item.item_name @@ -693,6 +703,15 @@ def get_itemised_tax_breakup_html(doc): ) ) +@frappe.whitelist() +def get_round_off_applicable_accounts(company, account_list): + account_list = get_regional_round_off_accounts(company, account_list) + + return account_list + +@erpnext.allow_regional +def get_regional_round_off_accounts(company, account_list): + pass @erpnext.allow_regional def update_itemised_tax_data(doc): diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 109d9216e7..347da6b563 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -399,6 +399,7 @@ regional_overrides = { 'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_header': 'erpnext.regional.india.utils.get_itemised_tax_breakup_header', 'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_data': 'erpnext.regional.india.utils.get_itemised_tax_breakup_data', 'erpnext.accounts.party.get_regional_address_details': 'erpnext.regional.india.utils.get_regional_address_details', + 'erpnext.controllers.taxes_and_totals.get_regional_round_off_accounts': 'erpnext.regional.india.utils.get_regional_round_off_accounts', 'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption', 'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period', 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries', diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 416495ceac..d81321b291 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -2,7 +2,9 @@ // License: GNU General Public License v3. See license.txt erpnext.taxes_and_totals = erpnext.payments.extend({ - setup: function() {}, + setup: function() { + this.fetch_round_off_accounts(); + }, apply_pricing_rule_on_item: function(item) { let effective_item_rate = item.price_list_rate; @@ -152,6 +154,22 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ }); }, + fetch_round_off_accounts: function() { + let me = this; + frappe.flags.round_off_applicable_accounts = []; + + return frappe.call({ + "method": "erpnext.controllers.taxes_and_totals.get_round_off_applicable_accounts", + "args": { + "company": me.frm.doc.company, + "account_list": frappe.flags.round_off_applicable_accounts + }, + callback: function(r) { + frappe.flags.round_off_applicable_accounts.push(...r.message); + } + }); + }, + determine_exclusive_rate: function() { var me = this; @@ -372,11 +390,21 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ } else if (tax.charge_type == "On Item Quantity") { current_tax_amount = tax_rate * item.qty; } + + current_tax_amount = this.get_final_tax_amount(tax, current_tax_amount); this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount); return current_tax_amount; }, + get_final_tax_amount: function(tax, current_tax_amount) { + if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) { + current_tax_amount = Math.round(current_tax_amount); + } + + return current_tax_amount; + }, + set_item_wise_tax: function(item, tax, tax_rate, current_tax_amount) { // store tax breakup for each item let tax_detail = tax.item_wise_tax_detail; diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.json b/erpnext/regional/doctype/gst_settings/gst_settings.json index 98c33ad33b..95b930c4c8 100644 --- a/erpnext/regional/doctype/gst_settings/gst_settings.json +++ b/erpnext/regional/doctype/gst_settings/gst_settings.json @@ -1,222 +1,86 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-06-27 15:09:01.318003", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2017-06-27 15:09:01.318003", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "gst_summary", + "column_break_2", + "round_off_gst_values", + "gstin_email_sent_on", + "section_break_4", + "gst_accounts", + "b2c_limit" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "gst_summary", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "GST Summary", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "gst_summary", + "fieldtype": "HTML", + "label": "GST Summary", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "gstin_email_sent_on", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "GSTIN Email Sent On", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "gstin_email_sent_on", + "fieldtype": "Date", + "label": "GSTIN Email Sent On", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "gst_accounts", - "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": "GST Accounts", - "length": 0, - "no_copy": 0, - "options": "GST Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "gst_accounts", + "fieldtype": "Table", + "label": "GST Accounts", + "options": "GST Account", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "250000", - "description": "Set Invoice Value for B2C. B2CL and B2CS calculated based on this invoice value.", - "fieldname": "b2c_limit", - "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": "B2C Limit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "default": "250000", + "description": "Set Invoice Value for B2C. B2CL and B2CS calculated based on this invoice value.", + "fieldname": "b2c_limit", + "fieldtype": "Data", + "in_list_view": 1, + "label": "B2C Limit", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "description": "Enabling this option will round off individual GST components in all the Invoices", + "fieldname": "round_off_gst_values", + "fieldtype": "Check", + "label": "Round Off GST Values", + "show_days": 1, + "show_seconds": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2018-02-14 08:14:15.375181", - "modified_by": "Administrator", - "module": "Regional", - "name": "GST Settings", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file + ], + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2021-01-28 17:19:47.969260", + "modified_by": "Administrator", + "module": "Regional", + "name": "GST Settings", + "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/gstr_3b_report/test_gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py index 8174da20cb..023b4ed22b 100644 --- a/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py @@ -14,8 +14,20 @@ import json test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"] class TestGSTR3BReport(unittest.TestCase): - def test_gstr_3b_report(self): + def setUp(self): + frappe.set_user("Administrator") + frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company GST'") + frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company GST'") + frappe.db.sql("delete from `tabGSTR 3B Report` where company='_Test Company GST'") + + make_company() + make_item("Milk", properties = {"is_nil_exempt": 1, "standard_rate": 0.000000}) + set_account_heads() + make_customers() + make_suppliers() + + def test_gstr_3b_report(self): month_number_mapping = { 1: "January", 2: "February", @@ -31,17 +43,6 @@ class TestGSTR3BReport(unittest.TestCase): 12: "December" } - frappe.set_user("Administrator") - - frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company GST'") - frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company GST'") - frappe.db.sql("delete from `tabGSTR 3B Report` where company='_Test Company GST'") - - make_company() - make_item("Milk", properties = {"is_nil_exempt": 1, "standard_rate": 0.000000}) - set_account_heads() - make_customers() - make_suppliers() make_sales_invoice() create_purchase_invoices() @@ -67,6 +68,42 @@ class TestGSTR3BReport(unittest.TestCase): self.assertEqual(output["itc_elg"]["itc_avl"][4]["samt"], 22.50) self.assertEqual(output["itc_elg"]["itc_avl"][4]["camt"], 22.50) + def test_gst_rounding(self): + gst_settings = frappe.get_doc('GST Settings') + gst_settings.round_off_gst_values = 1 + gst_settings.save() + + current_country = frappe.flags.country + frappe.flags.country = 'India' + + si = create_sales_invoice(company="_Test Company GST", + customer = '_Test GST Customer', + currency = 'INR', + warehouse = 'Finished Goods - _GST', + debit_to = 'Debtors - _GST', + income_account = 'Sales - _GST', + expense_account = 'Cost of Goods Sold - _GST', + cost_center = 'Main - _GST', + rate=216, + do_not_save=1 + ) + + si.append("taxes", { + "charge_type": "On Net Total", + "account_head": "IGST - _GST", + "cost_center": "Main - _GST", + "description": "IGST @ 18.0", + "rate": 18 + }) + + si.save() + # Check for 39 instead of 38.88 + self.assertEqual(si.taxes[0].base_tax_amount_after_discount_amount, 39) + + frappe.flags.country = current_country + gst_settings.round_off_gst_values = 1 + gst_settings.save() + def make_sales_invoice(): si = create_sales_invoice(company="_Test Company GST", customer = '_Test GST Customer', @@ -145,7 +182,6 @@ def make_sales_invoice(): si3.submit() def create_purchase_invoices(): - pi = make_purchase_invoice( company="_Test Company GST", supplier = '_Test Registered Supplier', @@ -193,7 +229,6 @@ def create_purchase_invoices(): pi1.submit() def make_suppliers(): - if not frappe.db.exists("Supplier", "_Test Registered Supplier"): frappe.get_doc({ "supplier_group": "_Test Supplier Group", @@ -257,7 +292,6 @@ def make_suppliers(): address.save() def make_customers(): - if not frappe.db.exists("Customer", "_Test GST Customer"): frappe.get_doc({ "customer_group": "_Test Customer Group", @@ -354,9 +388,9 @@ def make_customers(): address.save() def make_company(): - if frappe.db.exists("Company", "_Test Company GST"): return + company = frappe.new_doc("Company") company.company_name = "_Test Company GST" company.abbr = "_GST" @@ -388,7 +422,6 @@ def make_company(): address.save() def set_account_heads(): - gst_settings = frappe.get_doc("GST Settings") gst_account = frappe.get_all( diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index e89885f380..09bafdd7f2 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -772,3 +772,24 @@ def make_regional_gl_entries(gl_entries, doc): ) return gl_entries + +@frappe.whitelist() +def get_regional_round_off_accounts(company, account_list): + country = frappe.get_cached_value('Company', company, 'country') + + if country != 'India': + return + + if isinstance(account_list, string_types): + account_list = json.loads(account_list) + + if not frappe.db.get_single_value('GST Settings', 'round_off_gst_values'): + return + + gst_accounts = get_gst_accounts(company) + gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \ + + gst_accounts.get('igst_account') + + account_list.extend(gst_account_list) + + return account_list \ No newline at end of file From 8c7c682f1704fa51e8e125aafa08fd24212dc8e1 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 19 Feb 2021 20:35:23 +0530 Subject: [PATCH 699/728] chore: make parameter group optional --- .../quality_inspection_parameter.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json index 6d97601091..418b4825f2 100644 --- a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json +++ b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json @@ -29,13 +29,12 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Parameter Group", - "options": "Quality Inspection Parameter Group", - "reqd": 1 + "options": "Quality Inspection Parameter Group" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-02-04 18:45:12.486344", + "modified": "2021-02-19 20:33:30.657406", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection Parameter", From ab149f196eb200da41a7d0a00165da1b6f55a203 Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Sat, 20 Feb 2021 10:43:47 +0530 Subject: [PATCH 700/728] fix: typo in Leave Balance Report Change Leaves to Leave --- .../report/employee_leave_balance/employee_leave_balance.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index 1b92358184..06f9160363 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -40,17 +40,17 @@ def get_columns(): 'fieldname': 'opening_balance', 'width': 130, }, { - 'label': _('Leaves Allocated'), + 'label': _('Leave Allocated'), 'fieldtype': 'float', 'fieldname': 'leaves_allocated', 'width': 130, }, { - 'label': _('Leaves Taken'), + 'label': _('Leave Taken'), 'fieldtype': 'float', 'fieldname': 'leaves_taken', 'width': 130, }, { - 'label': _('Leaves Expired'), + 'label': _('Leave Expired'), 'fieldtype': 'float', 'fieldname': 'leaves_expired', 'width': 130, From 43f126ed1339a46f6a28a2755b6e0f587f0ed8a2 Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Sat, 20 Feb 2021 10:46:22 +0530 Subject: [PATCH 701/728] fix: Typo in Leave Application Change "Leaves" to "Leave" --- .../leave_application_dashboard.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html index 6324b04927..9f667a6835 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html +++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html @@ -4,11 +4,11 @@ {{ __("Leave Type") }} - {{ __("Total Allocated Leaves") }} - {{ __("Expired Leaves") }} - {{ __("Used Leaves") }} - {{ __("Pending Leaves") }} - {{ __("Available Leaves") }} + {{ __("Total Allocated Leave") }} + {{ __("Expired Leave") }} + {{ __("Used Leave") }} + {{ __("Pending Leave") }} + {{ __("Available Leave") }} @@ -25,5 +25,5 @@ {% else %} -

    No Leaves have been allocated.

    -{% endif %} \ No newline at end of file +

    No Leave has been allocated.

    +{% endif %} From cbb4fe2cce2993f30c8d269b8904c03bc1a48663 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 21 Feb 2021 20:57:30 +0530 Subject: [PATCH 702/728] fix: Add cards --- .../closed_loans/closed_loans.json | 23 ++++++++++++++++++ .../total_repayment/total_repayment.json | 24 +++++++++++++++++++ .../total_write_off/total_write_off.json | 24 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 erpnext/loan_management/number_card/closed_loans/closed_loans.json create mode 100644 erpnext/loan_management/number_card/total_repayment/total_repayment.json create mode 100644 erpnext/loan_management/number_card/total_write_off/total_write_off.json diff --git a/erpnext/loan_management/number_card/closed_loans/closed_loans.json b/erpnext/loan_management/number_card/closed_loans/closed_loans.json new file mode 100644 index 0000000000..c2f2244265 --- /dev/null +++ b/erpnext/loan_management/number_card/closed_loans/closed_loans.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "", + "creation": "2021-02-21 19:51:49.261813", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"status\",\"=\",\"Closed\",false]]", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Closed Loans", + "modified": "2021-02-21 19:51:54.087903", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Closed Loans", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/total_repayment/total_repayment.json b/erpnext/loan_management/number_card/total_repayment/total_repayment.json new file mode 100644 index 0000000000..38de42b89c --- /dev/null +++ b/erpnext/loan_management/number_card/total_repayment/total_repayment.json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "amount_paid", + "color": "#29CD42", + "creation": "2021-02-21 19:27:45.989222", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Repayment", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Repayment\",\"docstatus\",\"=\",\"1\",false]]", + "function": "Sum", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Total Repayment", + "modified": "2021-02-21 19:34:59.656546", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Total Repayment", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/total_write_off/total_write_off.json b/erpnext/loan_management/number_card/total_write_off/total_write_off.json new file mode 100644 index 0000000000..c85169acf8 --- /dev/null +++ b/erpnext/loan_management/number_card/total_write_off/total_write_off.json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "write_off_amount", + "color": "#CB2929", + "creation": "2021-02-21 19:48:29.004429", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Write Off", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Write Off\",\"docstatus\",\"=\",\"1\",false]]", + "function": "Sum", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Total Write Off", + "modified": "2021-02-21 19:48:58.604159", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Total Write Off", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file From e53fd8766991a0ba58066614c66cbf17b2ae26e7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 21 Feb 2021 20:57:56 +0530 Subject: [PATCH 703/728] fix: Rearrange cards --- .../loan_dashboard/loan_dashboard.json | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json b/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json index bd04cfe4aa..e060253d34 100644 --- a/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json +++ b/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json @@ -1,22 +1,25 @@ { "cards": [ { - "card": "Total Disbursed" - }, - { - "card": "Total Sanctioned Amount" + "card": "New Loans" }, { "card": "Active Loans" }, + { + "card": "Closed Loans" + }, + { + "card": "Total Disbursed" + }, { "card": "Open Loan Applications" }, { - "card": "New Loans" + "card": "New Loan Applications" }, { - "card": "New Loan Applications" + "card": "Total Sanctioned Amount" }, { "card": "Active Securities" @@ -26,6 +29,12 @@ }, { "card": "Total Shortfall Amount" + }, + { + "card": "Total Repayment" + }, + { + "card": "Total Write Off" } ], "charts": [ @@ -53,7 +62,7 @@ "idx": 0, "is_default": 0, "is_standard": 1, - "modified": "2021-02-18 20:07:10.095401", + "modified": "2021-02-21 20:53:47.531699", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Dashboard", From e4964a0c6ca1e01c6c68ed5715b067261bfe76ad Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 21 Feb 2021 21:03:42 +0530 Subject: [PATCH 704/728] fix: Update color in charts --- .../loan_interest_accrual/loan_interest_accrual.json | 4 +++- .../loan_management/dashboard_chart/new_loans/new_loans.json | 5 +++-- .../top_10_pledged_loan_securities.json | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json b/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json index 5b3c0e3dd3..aa0f78a2f6 100644 --- a/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json +++ b/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json @@ -2,6 +2,7 @@ "based_on": "posting_date", "chart_name": "Loan Interest Accrual", "chart_type": "Sum", + "color": "#39E4A5", "creation": "2021-02-18 20:07:04.843876", "docstatus": 0, "doctype": "Dashboard Chart", @@ -12,7 +13,8 @@ "idx": 0, "is_public": 0, "is_standard": 1, - "modified": "2021-02-18 20:07:10.015299", + "last_synced_on": "2021-02-21 21:01:26.022634", + "modified": "2021-02-21 21:01:44.930712", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Interest Accrual", diff --git a/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json b/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json index 6e196d25a2..35bd43b994 100644 --- a/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json +++ b/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json @@ -2,6 +2,7 @@ "based_on": "creation", "chart_name": "New Loans", "chart_type": "Count", + "color": "#449CF0", "creation": "2021-02-06 16:59:27.509170", "docstatus": 0, "doctype": "Dashboard Chart", @@ -12,8 +13,8 @@ "idx": 0, "is_public": 0, "is_standard": 1, - "last_synced_on": "2021-02-06 17:11:05.154562", - "modified": "2021-02-06 17:28:40.134346", + "last_synced_on": "2021-02-21 20:55:33.515025", + "modified": "2021-02-21 21:00:33.900821", "modified_by": "Administrator", "module": "Loan Management", "name": "New Loans", diff --git a/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json b/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json index d48a6ce083..76c27b062d 100644 --- a/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json +++ b/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json @@ -2,6 +2,7 @@ "based_on": "", "chart_name": "Top 10 Pledged Loan Securities", "chart_type": "Custom", + "color": "#EC864B", "creation": "2021-02-06 22:02:46.284479", "docstatus": 0, "doctype": "Dashboard Chart", @@ -12,7 +13,8 @@ "idx": 0, "is_public": 0, "is_standard": 1, - "modified": "2021-02-07 17:34:25.117983", + "last_synced_on": "2021-02-21 21:00:57.043034", + "modified": "2021-02-21 21:01:10.048623", "modified_by": "Administrator", "module": "Loan Management", "name": "Top 10 Pledged Loan Securities", From aa46def745f2e45bf80c2769ee8eae6e24089a8a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 21 Feb 2021 21:54:23 +0530 Subject: [PATCH 705/728] fix: Accounting Dimension creation background job timeout --- .../doctype/accounting_dimension/accounting_dimension.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index dd26c4cec2..774c58a820 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -43,11 +43,11 @@ class AccountingDimension(Document): if frappe.flags.in_test: make_dimension_in_accounting_doctypes(doc=self) else: - frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self) + frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self, queue='long') def on_trash(self): if frappe.flags.in_test: - delete_accounting_dimension(doc=self) + delete_accounting_dimension(doc=self, queue='long') else: frappe.enqueue(delete_accounting_dimension, doc=self) From d780f56ba8f66484b22746958ac1384b8d38bd2d Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 22 Feb 2021 15:12:35 +0530 Subject: [PATCH 706/728] Revert "fix: Error handling in loan reports and precision fixes" --- .../doctype/loan_interest_accrual/loan_interest_accrual.py | 4 +++- .../report/loan_security_exposure/loan_security_exposure.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 7978350adf..7d7992d40a 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -246,5 +246,7 @@ def get_per_day_interest(principal_amount, rate_of_interest, posting_date=None): if not posting_date: posting_date = getdate() - return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100)) + precision = cint(frappe.db.get_default("currency_precision")) or 2 + + return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100), precision) diff --git a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py index 8b6ea869a3..adc8013c68 100644 --- a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py +++ b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py @@ -76,7 +76,7 @@ def get_company_wise_loan_security_details(filters, loan_security_details): if qty: security_wise_map[key[1]]['applicant_count'] += 1 - total_portfolio_value += flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0)) + total_portfolio_value += flt(qty * loan_security_details.get(key[1])['latest_price']) return security_wise_map, total_portfolio_value From fdafb55fba5448b14d286b2ff0ddf7abc62e8f9e Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:25:57 +0530 Subject: [PATCH 707/728] fix: error message when making reverse journal entry (#24665) --- .../accounts/doctype/journal_entry/journal_entry.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index cb90f8036e..3419bb6c3e 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -102,7 +102,7 @@ class JournalEntry(AccountsController): if account_currency == previous_account_currency: if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit: frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry")) - + def validate_stock_accounts(self): stock_accounts = get_stock_accounts(self.company, self.doctype, self.name) for account in stock_accounts: @@ -229,11 +229,11 @@ class JournalEntry(AccountsController): if d.reference_type=="Journal Entry": account_root_type = frappe.db.get_value("Account", d.account, "root_type") if account_root_type == "Asset" and flt(d.debit) > 0: - frappe.throw(_("For {0}, only credit accounts can be linked against another debit entry") - .format(d.account)) + frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets credited") + .format(d.idx, d.account)) elif account_root_type == "Liability" and flt(d.credit) > 0: - frappe.throw(_("For {0}, only debit accounts can be linked against another credit entry") - .format(d.account)) + frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets debited") + .format(d.idx, d.account)) if d.reference_name == self.name: frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column")) @@ -1077,4 +1077,4 @@ def make_reverse_journal_entry(source_name, target_doc=None): }, }, target_doc) - return doclist \ No newline at end of file + return doclist From 14472f6bab275c0339bb0ad4581cc49e5f66a365 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 22 Feb 2021 19:27:23 +0530 Subject: [PATCH 708/728] fix(regional): vehicle no is mandatory for ewaybill generation (#24679) * fix: vehicle no required for e-invoice * fix: ewaybill generation dialog condition --- erpnext/patches.txt | 1 + .../patches/v13_0/update_vehicle_no_reqd_condition.py | 9 +++++++++ erpnext/regional/india/e_invoice/einvoice.js | 1 - 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e5ee551c11..01d9ad32e7 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -754,3 +754,4 @@ erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021 erpnext.patches.v12_0.add_state_code_for_ladakh erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl +erpnext.patches.v13_0.update_vehicle_no_reqd_condition \ No newline at end of file diff --git a/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py new file mode 100644 index 0000000000..33fef86af8 --- /dev/null +++ b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py @@ -0,0 +1,9 @@ +import frappe + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + if frappe.db.exists('Custom Field', { 'fieldname': 'vehicle_no' }): + frappe.db.set_value('Custom Field', { 'fieldname': 'vehicle_no' }, 'mandatory_depends_on', '') \ No newline at end of file diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js index 9fa94c401f..a756b57eb7 100644 --- a/erpnext/regional/india/e_invoice/einvoice.js +++ b/erpnext/regional/india/e_invoice/einvoice.js @@ -188,7 +188,6 @@ const get_ewaybill_fields = (frm) => { 'fieldname': 'vehicle_no', 'label': 'Vehicle No', 'fieldtype': 'Data', - 'depends_on': 'eval:(doc.mode_of_transport === "Road")', 'default': frm.doc.vehicle_no }, { From 8676bedd18aae126fea412adbc7cb98859643b77 Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:28:01 +0530 Subject: [PATCH 709/728] fix: Add phone icon on read only phone fields (#24680) * fix: Add phone icon on read only phone fields * fix: sider fix --- erpnext/public/js/telephony.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/telephony.js b/erpnext/public/js/telephony.js index 6cb1207e79..b66126c2b8 100644 --- a/erpnext/public/js/telephony.js +++ b/erpnext/public/js/telephony.js @@ -1,13 +1,16 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlData.extend( { make_input() { - this._super(); + if (!this.df.read_only) { + this._super(); + } if (this.df.options == 'Phone') { this.setup_phone(); } }, setup_phone() { if (frappe.phone_call.handler) { - this.$wrapper.find('.control-input') + let control = this.df.read_only ? '.control-value' : '.control-input'; + this.$wrapper.find(control) .append(` From bb8cd1cdaac12ea405fb1ed36656a2b535164192 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:28:45 +0530 Subject: [PATCH 710/728] feat: Basic validation for indian PAN No. in Supplier (#24687) --- erpnext/hooks.py | 3 +++ erpnext/regional/india/utils.py | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 347da6b563..39d3659b2b 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -272,6 +272,9 @@ doc_events = { 'Address': { 'validate': ['erpnext.regional.india.utils.validate_gstin_for_india', 'erpnext.regional.italy.utils.set_state_code', 'erpnext.regional.india.utils.update_gst_category'] }, + 'Supplier': { + 'validate': 'erpnext.regional.india.utils.validate_pan_for_india' + }, ('Sales Invoice', 'Sales Order', 'Delivery Note', 'Purchase Invoice', 'Purchase Order', 'Purchase Receipt'): { 'validate': ['erpnext.regional.india.utils.set_place_of_supply'] }, diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 09bafdd7f2..5f078632d6 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -55,6 +55,14 @@ 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_pan_for_india(doc, method): + if doc.get('country') != 'India': + return + + p = re.compile("[A-Z]{5}[0-9]{4}[A-Z]{1}") + if not p.match(doc.pan): + frappe.throw(_("Invalid PAN No. The input you've entered doesn't match the format of PAN.")) + 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: From 98a0feab896b8b98d5cc697b1774ed4a965dad5a Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 22 Feb 2021 19:30:02 +0530 Subject: [PATCH 711/728] fix(india): escape for special characters in JSON (#24695) JSON does not accept special whitespace characters like tab, carriage return, line feed Ref: https://www.ecma-international.org/wp-content/uploads/ECMA-404_2nd_edition_december_2017.pdf Related issue: ISS-20-21-09811 --- erpnext/regional/india/e_invoice/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index 2043f49825..322fa183f4 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -160,7 +160,7 @@ def get_item_list(invoice): item.update(d.as_dict()) item.sr_no = d.idx - item.description = d.item_name.replace('"', '\\"') + item.description = json.dumps(d.item_name)[1:-1] item.qty = abs(item.qty) item.discount_amount = 0 From 0ca74ce04bb6c54bf458cc395cceab49e784b36c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Feb 2021 19:32:51 +0530 Subject: [PATCH 712/728] fix: Fixes related to client script renaming (#24698) --- erpnext/patches/v11_0/refactor_autoname_naming.py | 2 +- ...eplace_renamed_fields_in_custom_scripts_and_print_formats.py | 2 +- erpnext/patches/v7_0/remove_doctypes_and_reports.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v11_0/refactor_autoname_naming.py b/erpnext/patches/v11_0/refactor_autoname_naming.py index 5dc5d3bf0c..b997ba2db2 100644 --- a/erpnext/patches/v11_0/refactor_autoname_naming.py +++ b/erpnext/patches/v11_0/refactor_autoname_naming.py @@ -20,7 +20,7 @@ doctype_series_map = { 'Certified Consultant': 'NPO-CONS-.YYYY.-.#####', 'Chat Room': 'CHAT-ROOM-.#####', 'Compensatory Leave Request': 'HR-CMP-.YY.-.MM.-.#####', - 'Custom Script': 'SYS-SCR-.#####', + 'Client Script': 'SYS-SCR-.#####', 'Employee Benefit Application': 'HR-BEN-APP-.YY.-.MM.-.#####', 'Employee Benefit Application Detail': '', 'Employee Benefit Claim': 'HR-BEN-CLM-.YY.-.MM.-.#####', diff --git a/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py b/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py index ef3f1d6c0a..c564f8b02a 100644 --- a/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py +++ b/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py @@ -9,7 +9,7 @@ def execute(): # NOTE: sequence is important renamed_fields = get_all_renamed_fields() - for dt, script_field, ref_dt_field in (("Custom Script", "script", "dt"), ("Print Format", "html", "doc_type")): + for dt, script_field, ref_dt_field in (("Client Script", "script", "dt"), ("Print Format", "html", "doc_type")): cond1 = " or ".join("""{0} like "%%{1}%%" """.format(script_field, d[0].replace("_", "\\_")) for d in renamed_fields) cond2 = " and standard = 'No'" if dt == "Print Format" else "" diff --git a/erpnext/patches/v7_0/remove_doctypes_and_reports.py b/erpnext/patches/v7_0/remove_doctypes_and_reports.py index 746cae0e1c..2356e2f6ee 100644 --- a/erpnext/patches/v7_0/remove_doctypes_and_reports.py +++ b/erpnext/patches/v7_0/remove_doctypes_and_reports.py @@ -7,7 +7,7 @@ def execute(): where name in('Time Log Batch', 'Time Log Batch Detail', 'Time Log')""") frappe.db.sql("""delete from `tabDocField` where parent in ('Time Log', 'Time Log Batch')""") - frappe.db.sql("""update `tabCustom Script` set dt = 'Timesheet' where dt = 'Time Log'""") + frappe.db.sql("""update `tabClient Script` set dt = 'Timesheet' where dt = 'Time Log'""") for data in frappe.db.sql(""" select label, fieldname from `tabCustom Field` where dt = 'Time Log'""", as_dict=1): custom_field = frappe.get_doc({ From 58399f652470254af0cbc743a0c8fe7f586c12df Mon Sep 17 00:00:00 2001 From: Shridhar Patil Date: Mon, 22 Feb 2021 19:37:38 +0530 Subject: [PATCH 713/728] fix: next follow up date should be always greater then current time (#24308) --- erpnext/crm/doctype/opportunity/opportunity.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 08958b7dd6..ac374a95f4 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -24,6 +24,12 @@ frappe.ui.form.on("Opportunity", { frm.trigger('set_contact_link'); } }, + contact_date: function(frm) { + if(frm.doc.contact_date < frappe.datetime.now_datetime()){ + frm.set_value("contact_date", ""); + frappe.throw(__("Next follow up date should be greater than now.")) + } + }, onload_post_render: function(frm) { frm.get_field("items").grid.set_multiple_add("item_code", "qty"); From 69a6bed10621db660325eb5b3a4b36d5d5c56a77 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Feb 2021 20:03:23 +0530 Subject: [PATCH 714/728] fix: Replaced spaces with tabs --- erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py index 33fef86af8..c26cddbe4e 100644 --- a/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py +++ b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py @@ -1,9 +1,9 @@ import frappe def execute(): - company = frappe.get_all('Company', filters = {'country': 'India'}) + company = frappe.get_all('Company', filters = {'country': 'India'}) if not company: return - if frappe.db.exists('Custom Field', { 'fieldname': 'vehicle_no' }): - frappe.db.set_value('Custom Field', { 'fieldname': 'vehicle_no' }, 'mandatory_depends_on', '') \ No newline at end of file + if frappe.db.exists('Custom Field', { 'fieldname': 'vehicle_no' }): + frappe.db.set_value('Custom Field', { 'fieldname': 'vehicle_no' }, 'mandatory_depends_on', '') From 866cf70d3ac2b8a17a9016e8cc00dca881461a61 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Feb 2021 21:35:00 +0530 Subject: [PATCH 715/728] fix: validate pan --- erpnext/regional/india/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 5f078632d6..cb30605291 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -56,7 +56,7 @@ def validate_gstin_for_india(doc, method): .format(doc.gst_state_number)) def validate_pan_for_india(doc, method): - if doc.get('country') != 'India': + if doc.get('country') != 'India' or not doc.pan: return p = re.compile("[A-Z]{5}[0-9]{4}[A-Z]{1}") From 07f40596bce6c6703b6f151be09161b7d61213ce Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Mon, 22 Feb 2021 22:19:47 +0530 Subject: [PATCH 716/728] feat(Bank Reconciliation): Redesign (#24273) Co-authored-by: Nabin Hait --- .../doctype/bank_account/bank_account.json | 3 +- .../__init__.py | 0 .../bank_reconciliation_tool.js | 162 ++++ .../bank_reconciliation_tool.json | 113 +++ .../bank_reconciliation_tool.py | 452 ++++++++++ .../test_bank_reconciliation_tool.py | 10 + .../__init__.py | 0 .../bank_statement_import.css | 3 + .../bank_statement_import.js | 574 +++++++++++++ .../bank_statement_import.json | 227 +++++ .../bank_statement_import.py | 205 +++++ .../bank_statement_import_list.js | 36 + .../test_bank_statement_import.py | 10 + .../bank_statement_settings.js | 8 - .../bank_statement_settings.json | 272 ------ .../bank_statement_settings.py | 11 - .../test_bank_statement_settings.js | 23 - .../test_bank_statement_settings.py | 10 - .../bank_statement_settings_item.json | 101 --- .../bank_statement_settings_item.py | 10 - .../__init__.py | 0 .../bank_statement_transaction_entry.js | 100 --- .../bank_statement_transaction_entry.json | 792 ------------------ .../bank_statement_transaction_entry.py | 443 ---------- .../test_bank_statement_transaction_entry.js | 23 - .../test_bank_statement_transaction_entry.py | 10 - .../__init__.py | 0 ...nk_statement_transaction_invoice_item.json | 365 -------- ...bank_statement_transaction_invoice_item.py | 10 - .../__init__.py | 0 ...nk_statement_transaction_payment_item.json | 494 ----------- ...bank_statement_transaction_payment_item.py | 10 - .../__init__.py | 0 .../bank_statement_transaction_settings.js | 8 - .../bank_statement_transaction_settings.json | 266 ------ .../bank_statement_transaction_settings.py | 11 - ...est_bank_statement_transaction_settings.js | 23 - ...est_bank_statement_transaction_settings.py | 10 - .../__init__.py | 0 ...k_statement_transaction_settings_item.json | 166 ---- ...ank_statement_transaction_settings_item.py | 10 - .../bank_transaction/bank_transaction.js | 66 +- .../bank_transaction/bank_transaction.json | 756 ++--------------- .../bank_transaction/bank_transaction.py | 24 +- .../bank_transaction/test_bank_transaction.py | 78 +- .../page/bank_reconciliation/__init__.py | 0 .../bank_reconciliation.js | 583 ------------- .../bank_reconciliation.json | 29 - .../bank_reconciliation.py | 369 -------- .../bank_transaction_header.html | 21 - .../bank_transaction_row.html | 36 - .../linked_payment_header.html | 21 - .../linked_payment_row.html | 36 - .../doctype/plaid_settings/plaid_settings.py | 4 +- erpnext/patches.txt | 3 +- .../v11_1/update_bank_transaction_status.py | 23 +- ...delete_old_bank_reconciliation_doctypes.py | 26 + erpnext/public/build.json | 5 + .../data_table_manager.js | 220 +++++ .../dialog_manager.js | 594 +++++++++++++ .../bank_reconciliation_tool/number_card.js | 75 ++ 61 files changed, 2922 insertions(+), 5018 deletions(-) rename erpnext/accounts/doctype/{bank_statement_settings => bank_reconciliation_tool}/__init__.py (100%) create mode 100644 erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js create mode 100644 erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json create mode 100644 erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py create mode 100644 erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py rename erpnext/accounts/doctype/{bank_statement_settings_item => bank_statement_import}/__init__.py (100%) create mode 100644 erpnext/accounts/doctype/bank_statement_import/bank_statement_import.css create mode 100644 erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js create mode 100644 erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json create mode 100644 erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py create mode 100644 erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js create mode 100644 erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py delete mode 100644 erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.js delete mode 100644 erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.json delete mode 100644 erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.py delete mode 100644 erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.js delete mode 100644 erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.py delete mode 100644 erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.json delete mode 100644 erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.py delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_entry/__init__.py delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.js delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.json delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.js delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.py delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_invoice_item/__init__.py delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.json delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.py delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_payment_item/__init__.py delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.json delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.py delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_settings/__init__.py delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.js delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.json delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.py delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.js delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.py delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_settings_item/__init__.py delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.json delete mode 100644 erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.py delete mode 100644 erpnext/accounts/page/bank_reconciliation/__init__.py delete mode 100644 erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js delete mode 100644 erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json delete mode 100644 erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py delete mode 100644 erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html delete mode 100644 erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html delete mode 100644 erpnext/accounts/page/bank_reconciliation/linked_payment_header.html delete mode 100644 erpnext/accounts/page/bank_reconciliation/linked_payment_row.html create mode 100644 erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py create mode 100644 erpnext/public/js/bank_reconciliation_tool/data_table_manager.js create mode 100644 erpnext/public/js/bank_reconciliation_tool/dialog_manager.js create mode 100644 erpnext/public/js/bank_reconciliation_tool/number_card.js diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json index b42f1f9d58..de67ab1ce5 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.json +++ b/erpnext/accounts/doctype/bank_account/bank_account.json @@ -86,6 +86,7 @@ }, { "default": "0", + "description": "Setting the account as a Company Account is necessary for Bank Reconciliation", "fieldname": "is_company_account", "fieldtype": "Check", "label": "Is Company Account" @@ -207,7 +208,7 @@ } ], "links": [], - "modified": "2020-07-17 13:59:50.795412", + "modified": "2020-10-23 16:48:06.303658", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Account", diff --git a/erpnext/accounts/doctype/bank_statement_settings/__init__.py b/erpnext/accounts/doctype/bank_reconciliation_tool/__init__.py similarity index 100% rename from erpnext/accounts/doctype/bank_statement_settings/__init__.py rename to erpnext/accounts/doctype/bank_reconciliation_tool/__init__.py diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js new file mode 100644 index 0000000000..297dd4333f --- /dev/null +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js @@ -0,0 +1,162 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +frappe.provide("erpnext.accounts.bank_reconciliation"); + +frappe.ui.form.on("Bank Reconciliation Tool", { + setup: function (frm) { + frm.set_query("bank_account", function () { + return { + filters: { + company: ["in", frm.doc.company], + }, + }; + }); + }, + + refresh: function (frm) { + frappe.require("assets/js/bank-reconciliation-tool.min.js", () => + frm.trigger("make_reconciliation_tool") + ); + frm.upload_statement_button = frm.page.set_secondary_action( + __("Upload Bank Statement"), + () => + frappe.call({ + method: + "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement", + args: { + dt: frm.doc.doctype, + dn: frm.doc.name, + company: frm.doc.company, + bank_account: frm.doc.bank_account, + }, + callback: function (r) { + if (!r.exc) { + var doc = frappe.model.sync(r.message); + frappe.set_route( + "Form", + doc[0].doctype, + doc[0].name + ); + } + }, + }) + ); + }, + + after_save: function (frm) { + frm.trigger("make_reconciliation_tool"); + }, + + bank_account: function (frm) { + frappe.db.get_value( + "Bank Account", + frm.bank_account, + "account", + (r) => { + frappe.db.get_value( + "Account", + r.account, + "account_currency", + (r) => { + frm.currency = r.account_currency; + } + ); + } + ); + frm.trigger("get_account_opening_balance"); + }, + + bank_statement_from_date: function (frm) { + frm.trigger("get_account_opening_balance"); + }, + + make_reconciliation_tool(frm) { + frm.get_field("reconciliation_tool_cards").$wrapper.empty(); + if (frm.doc.bank_account && frm.doc.bank_statement_to_date) { + frm.trigger("get_cleared_balance").then(() => { + if ( + frm.doc.bank_account && + frm.doc.bank_statement_from_date && + frm.doc.bank_statement_to_date && + frm.doc.bank_statement_closing_balance + ) { + frm.trigger("render_chart"); + frm.trigger("render"); + frappe.utils.scroll_to( + frm.get_field("reconciliation_tool_cards").$wrapper, + true, + 30 + ); + } + }); + } + }, + + get_account_opening_balance(frm) { + if (frm.doc.bank_account && frm.doc.bank_statement_from_date) { + frappe.call({ + method: + "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance", + args: { + bank_account: frm.doc.bank_account, + till_date: frm.doc.bank_statement_from_date, + }, + callback: (response) => { + frm.set_value("account_opening_balance", response.message); + }, + }); + } + }, + + get_cleared_balance(frm) { + if (frm.doc.bank_account && frm.doc.bank_statement_to_date) { + return frappe.call({ + method: + "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance", + args: { + bank_account: frm.doc.bank_account, + till_date: frm.doc.bank_statement_to_date, + }, + callback: (response) => { + frm.cleared_balance = response.message; + }, + }); + } + }, + + render_chart(frm) { + frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager( + { + $reconciliation_tool_cards: frm.get_field( + "reconciliation_tool_cards" + ).$wrapper, + bank_statement_closing_balance: + frm.doc.bank_statement_closing_balance, + cleared_balance: frm.cleared_balance, + currency: frm.currency, + } + ); + }, + + render(frm) { + if (frm.doc.bank_account) { + frm.bank_reconciliation_data_table_manager = new erpnext.accounts.bank_reconciliation.DataTableManager( + { + company: frm.doc.company, + bank_account: frm.doc.bank_account, + $reconciliation_tool_dt: frm.get_field( + "reconciliation_tool_dt" + ).$wrapper, + $no_bank_transactions: frm.get_field( + "no_bank_transactions" + ).$wrapper, + bank_statement_from_date: frm.doc.bank_statement_from_date, + bank_statement_to_date: frm.doc.bank_statement_to_date, + bank_statement_closing_balance: + frm.doc.bank_statement_closing_balance, + cards_manager: frm.cards_manager, + } + ); + } + }, +}); diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json new file mode 100644 index 0000000000..4837db3b86 --- /dev/null +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json @@ -0,0 +1,113 @@ +{ + "actions": [], + "creation": "2020-12-02 10:13:02.148040", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "bank_account", + "column_break_1", + "bank_statement_from_date", + "bank_statement_to_date", + "column_break_2", + "account_opening_balance", + "bank_statement_closing_balance", + "section_break_1", + "reconciliation_tool_cards", + "reconciliation_tool_dt", + "no_bank_transactions" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "fieldname": "bank_account", + "fieldtype": "Link", + "label": "Bank Account", + "options": "Bank Account" + }, + { + "fieldname": "column_break_1", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval: doc.bank_account", + "fieldname": "bank_statement_from_date", + "fieldtype": "Date", + "label": "Bank Statement From Date" + }, + { + "depends_on": "eval: doc.bank_statement_from_date", + "fieldname": "bank_statement_to_date", + "fieldtype": "Date", + "label": "Bank Statement To Date" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval: doc.bank_statement_from_date", + "fieldname": "account_opening_balance", + "fieldtype": "Currency", + "label": "Account Opening Balance", + "options": "Currency", + "read_only": 1 + }, + { + "depends_on": "eval: doc.bank_statement_to_date", + "fieldname": "bank_statement_closing_balance", + "fieldtype": "Currency", + "label": "Bank Statement Closing Balance", + "options": "Currency" + }, + { + "depends_on": "eval: doc.bank_statement_closing_balance", + "fieldname": "section_break_1", + "fieldtype": "Section Break", + "label": "Reconcile" + }, + { + "fieldname": "reconciliation_tool_cards", + "fieldtype": "HTML" + }, + { + "fieldname": "reconciliation_tool_dt", + "fieldtype": "HTML" + }, + { + "fieldname": "no_bank_transactions", + "fieldtype": "HTML", + "options": "
    No Matching Bank Transactions Found
    " + } + ], + "hide_toolbar": 1, + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2021-02-02 01:35:53.043578", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Bank Reconciliation Tool", + "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" +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py new file mode 100644 index 0000000000..8a17233cf7 --- /dev/null +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -0,0 +1,452 @@ +# -*- 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 json + +import frappe +from frappe.model.document import Document +from frappe import _ +from frappe.utils import flt + +from erpnext import get_company_currency +from erpnext.accounts.utils import get_balance_on +from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import get_entries, get_amounts_not_reflected_in_system +from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount + + +class BankReconciliationTool(Document): + pass + +@frappe.whitelist() +def get_bank_transactions(bank_account, from_date = None, to_date = None): + # returns bank transactions for a bank account + filters = [] + filters.append(['bank_account', '=', bank_account]) + filters.append(['docstatus', '=', 1]) + filters.append(['unallocated_amount', '>', 0]) + if to_date: + filters.append(['date', '<=', to_date]) + if from_date: + filters.append(['date', '>=', from_date]) + transactions = frappe.get_all( + 'Bank Transaction', + fields = ['date', 'deposit', 'withdrawal', 'currency', + 'description', 'name', 'bank_account', 'company', + 'unallocated_amount', 'reference_number', 'party_type', 'party'], + filters = filters + ) + return transactions + +@frappe.whitelist() +def get_account_balance(bank_account, till_date): + # returns account balance till the specified date + account = frappe.db.get_value('Bank Account', bank_account, 'account') + filters = frappe._dict({ + "account": account, + "report_date": till_date, + "include_pos_transactions": 1 + }) + data = get_entries(filters) + + balance_as_per_system = get_balance_on(filters["account"], filters["report_date"]) + + total_debit, total_credit = 0,0 + for d in data: + total_debit += flt(d.debit) + total_credit += flt(d.credit) + + amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters) + + bank_bal = flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) \ + + amounts_not_reflected_in_system + + return bank_bal + + +@frappe.whitelist() +def update_bank_transaction(bank_transaction_name, reference_number, party_type=None, party=None): + # updates bank transaction based on the new parameters provided by the user from Vouchers + bank_transaction = frappe.get_doc("Bank Transaction", bank_transaction_name) + bank_transaction.reference_number = reference_number + bank_transaction.party_type = party_type + bank_transaction.party = party + bank_transaction.save() + return frappe.db.get_all('Bank Transaction', + filters={ + 'name': bank_transaction_name + }, + fields=['date', 'deposit', 'withdrawal', 'currency', + 'description', 'name', 'bank_account', 'company', + 'unallocated_amount', 'reference_number', + 'party_type', 'party'], + )[0] + + +@frappe.whitelist() +def create_journal_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, posting_date=None, entry_type=None, + second_account=None, mode_of_payment=None, party_type=None, party=None, allow_edit=None): + # Create a new journal entry based on the bank transaction + bank_transaction = frappe.db.get_values( + "Bank Transaction", bank_transaction_name, + fieldname=["name", "deposit", "withdrawal", "bank_account"] , + as_dict=True + )[0] + company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account") + account_type = frappe.db.get_value("Account", second_account, "account_type") + if account_type in ["Receivable", "Payable"]: + if not (party_type and party): + frappe.throw(_("Party Type and Party is required for Receivable / Payable account {0}").format( second_account)) + accounts = [] + # Multi Currency? + accounts.append({ + "account": second_account, + "credit_in_account_currency": bank_transaction.deposit + if bank_transaction.deposit > 0 + else 0, + "debit_in_account_currency":bank_transaction.withdrawal + if bank_transaction.withdrawal > 0 + else 0, + "party_type":party_type, + "party":party, + }) + + accounts.append({ + "account": company_account, + "bank_account": bank_transaction.bank_account, + "credit_in_account_currency": bank_transaction.withdrawal + if bank_transaction.withdrawal > 0 + else 0, + "debit_in_account_currency":bank_transaction.deposit + if bank_transaction.deposit > 0 + else 0, + }) + + company = frappe.get_value("Account", company_account, "company") + + journal_entry_dict = { + "voucher_type" : entry_type, + "company" : company, + "posting_date" : posting_date, + "cheque_date" : reference_date, + "cheque_no" : reference_number, + "mode_of_payment" : mode_of_payment + } + journal_entry = frappe.new_doc('Journal Entry') + journal_entry.update(journal_entry_dict) + journal_entry.set("accounts", accounts) + + + if allow_edit: + return journal_entry + + journal_entry.insert() + journal_entry.submit() + + if bank_transaction.deposit > 0: + paid_amount = bank_transaction.deposit + else: + paid_amount = bank_transaction.withdrawal + + vouchers = json.dumps([{ + "payment_doctype":"Journal Entry", + "payment_name":journal_entry.name, + "amount":paid_amount}]) + + return reconcile_vouchers(bank_transaction.name, vouchers) + +@frappe.whitelist() +def create_payment_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, party_type=None, party=None, posting_date=None, + mode_of_payment=None, project=None, cost_center=None, allow_edit=None): + # Create a new payment entry based on the bank transaction + bank_transaction = frappe.db.get_values( + "Bank Transaction", bank_transaction_name, + fieldname=["name", "unallocated_amount", "deposit", "bank_account"] , + as_dict=True + )[0] + paid_amount = bank_transaction.unallocated_amount + payment_type = "Receive" if bank_transaction.deposit > 0 else "Pay" + + company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account") + company = frappe.get_value("Account", company_account, "company") + payment_entry_dict = { + "company" : company, + "payment_type" : payment_type, + "reference_no" : reference_number, + "reference_date" : reference_date, + "party_type" : party_type, + "party" : party, + "posting_date" : posting_date, + "paid_amount": paid_amount, + "received_amount": paid_amount + } + payment_entry = frappe.new_doc("Payment Entry") + + + payment_entry.update(payment_entry_dict) + + if mode_of_payment: + payment_entry.mode_of_payment = mode_of_payment + if project: + payment_entry.project = project + if cost_center: + payment_entry.cost_center = cost_center + if payment_type == "Receive": + payment_entry.paid_to = company_account + else: + payment_entry.paid_from = company_account + + payment_entry.validate() + + if allow_edit: + return payment_entry + + payment_entry.insert() + + payment_entry.submit() + vouchers = json.dumps([{ + "payment_doctype":"Payment Entry", + "payment_name":payment_entry.name, + "amount":paid_amount}]) + return reconcile_vouchers(bank_transaction.name, vouchers) + +@frappe.whitelist() +def reconcile_vouchers(bank_transaction_name, vouchers): + # updated clear date of all the vouchers based on the bank transaction + vouchers = json.loads(vouchers) + transaction = frappe.get_doc("Bank Transaction", bank_transaction_name) + if transaction.unallocated_amount == 0: + frappe.throw(_("This bank transaction is already fully reconciled")) + total_amount = 0 + for voucher in vouchers: + voucher['payment_entry'] = frappe.get_doc(voucher['payment_doctype'], voucher['payment_name']) + total_amount += get_paid_amount(frappe._dict({ + 'payment_document': voucher['payment_doctype'], + 'payment_entry': voucher['payment_name'], + }), transaction.currency) + + if total_amount > transaction.unallocated_amount: + frappe.throw(_("The Sum Total of Amounts of All Selected Vouchers Should be Less than the Unallocated Amount of the Bank Transaction")) + account = frappe.db.get_value("Bank Account", transaction.bank_account, "account") + + for voucher in vouchers: + gl_entry = frappe.db.get_value("GL Entry", dict(account=account, voucher_type=voucher['payment_doctype'], voucher_no=voucher['payment_name']), ['credit', 'debit'], as_dict=1) + gl_amount, transaction_amount = (gl_entry.credit, transaction.deposit) if gl_entry.credit > 0 else (gl_entry.debit, transaction.withdrawal) + allocated_amount = gl_amount if gl_amount >= transaction_amount else transaction_amount + + transaction.append("payment_entries", { + "payment_document": voucher['payment_entry'].doctype, + "payment_entry": voucher['payment_entry'].name, + "allocated_amount": allocated_amount + }) + + transaction.save() + transaction.update_allocations() + return frappe.get_doc("Bank Transaction", bank_transaction_name) + +@frappe.whitelist() +def get_linked_payments(bank_transaction_name, document_types = None): + # get all matching payments for a bank transaction + transaction = frappe.get_doc("Bank Transaction", bank_transaction_name) + bank_account = frappe.db.get_values( + "Bank Account", + transaction.bank_account, + ["account", "company"], + as_dict=True)[0] + (account, company) = (bank_account.account, bank_account.company) + matching = check_matching(account, company, transaction, document_types) + return matching + +def check_matching(bank_account, company, transaction, document_types): + # combine all types of vocuhers + subquery = get_queries(bank_account, company, transaction, document_types) + filters = { + "amount": transaction.unallocated_amount, + "payment_type" : "Receive" if transaction.deposit > 0 else "Pay", + "reference_no": transaction.reference_number, + "party_type": transaction.party_type, + "party": transaction.party, + "bank_account": bank_account + } + + matching_vouchers = [] + for query in subquery: + matching_vouchers.extend( + frappe.db.sql(query, filters,) + ) + + return sorted(matching_vouchers, key = lambda x: x[0], reverse=True) if matching_vouchers else [] + +def get_queries(bank_account, company, transaction, document_types): + # get queries to get matching vouchers + amount_condition = "=" if "exact_match" in document_types else "<=" + account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from" + queries = [] + + if "payment_entry" in document_types: + pe_amount_matching = get_pe_matching_query(amount_condition, account_from_to, transaction) + queries.extend([pe_amount_matching]) + + if "journal_entry" in document_types: + je_amount_matching = get_je_matching_query(amount_condition, transaction) + queries.extend([je_amount_matching]) + + if transaction.deposit > 0 and "sales_invoice" in document_types: + si_amount_matching = get_si_matching_query(amount_condition) + queries.extend([si_amount_matching]) + + if transaction.withdrawal > 0: + if "purchase_invoice" in document_types: + pi_amount_matching = get_pi_matching_query(amount_condition) + queries.extend([pi_amount_matching]) + + if "expense_claim" in document_types: + ec_amount_matching = get_ec_matching_query(bank_account, company, amount_condition) + queries.extend([ec_amount_matching]) + + return queries + +def get_pe_matching_query(amount_condition, account_from_to, transaction): + # get matching payment entries query + if transaction.deposit > 0: + currency_field = "paid_to_account_currency as currency" + else: + currency_field = "paid_from_account_currency as currency" + return f""" + SELECT + (CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END + + CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END + + 1 ) AS rank, + 'Payment Entry' as doctype, + name, + paid_amount, + reference_no, + reference_date, + party, + party_type, + posting_date, + {currency_field} + FROM + `tabPayment Entry` + WHERE + paid_amount {amount_condition} %(amount)s + AND docstatus = 1 + AND payment_type IN (%(payment_type)s, 'Internal Transfer') + AND ifnull(clearance_date, '') = "" + AND {account_from_to} = %(bank_account)s + """ + + +def get_je_matching_query(amount_condition, transaction): + # get matching journal entry query + cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit" + return f""" + + SELECT + (CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END + + 1) AS rank , + 'Journal Entry' as doctype, + je.name, + jea.{cr_or_dr}_in_account_currency as paid_amount, + je.cheque_no as reference_no, + je.cheque_date as reference_date, + je.pay_to_recd_from as party, + jea.party_type, + je.posting_date, + jea.account_currency as currency + FROM + `tabJournal Entry Account` as jea + JOIN + `tabJournal Entry` as je + ON + jea.parent = je.name + WHERE + (je.clearance_date is null or je.clearance_date='0000-00-00') + AND jea.account = %(bank_account)s + AND jea.{cr_or_dr}_in_account_currency {amount_condition} %(amount)s + AND je.docstatus = 1 + """ + + +def get_si_matching_query(amount_condition): + # get matchin sales invoice query + return f""" + SELECT + ( CASE WHEN si.customer = %(party)s THEN 1 ELSE 0 END + + 1 ) AS rank, + 'Sales Invoice' as doctype, + si.name, + sip.amount as paid_amount, + '' as reference_no, + '' as reference_date, + si.customer as party, + 'Customer' as party_type, + si.posting_date, + si.currency + + FROM + `tabSales Invoice Payment` as sip + JOIN + `tabSales Invoice` as si + ON + sip.parent = si.name + WHERE (sip.clearance_date is null or sip.clearance_date='0000-00-00') + AND sip.account = %(bank_account)s + AND sip.amount {amount_condition} %(amount)s + AND si.docstatus = 1 + """ + +def get_pi_matching_query(amount_condition): + # get matching purchase invoice query + return f""" + SELECT + ( CASE WHEN supplier = %(party)s THEN 1 ELSE 0 END + + 1 ) AS rank, + 'Purchase Invoice' as doctype, + name, + paid_amount, + '' as reference_no, + '' as reference_date, + supplier as party, + 'Supplier' as party_type, + posting_date, + currency + FROM + `tabPurchase Invoice` + WHERE + paid_amount {amount_condition} %(amount)s + AND docstatus = 1 + AND is_paid = 1 + AND ifnull(clearance_date, '') = "" + AND cash_bank_account = %(bank_account)s + """ + +def get_ec_matching_query(bank_account, company, amount_condition): + # get matching Expense Claim query + mode_of_payments = [x["parent"] for x in frappe.db.get_list("Mode of Payment Account", + filters={"default_account": bank_account}, fields=["parent"])] + mode_of_payments = '(\'' + '\', \''.join(mode_of_payments) + '\' )' + company_currency = get_company_currency(company) + return f""" + SELECT + ( CASE WHEN employee = %(party)s THEN 1 ELSE 0 END + + 1 ) AS rank, + 'Expense Claim' as doctype, + name, + total_sanctioned_amount as paid_amount, + '' as reference_no, + '' as reference_date, + employee as party, + 'Employee' as party_type, + posting_date, + '{company_currency}' as currency + FROM + `tabExpense Claim` + WHERE + total_sanctioned_amount {amount_condition} %(amount)s + AND docstatus = 1 + AND is_paid = 1 + AND ifnull(clearance_date, '') = "" + AND mode_of_payment in {mode_of_payments} + """ diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py new file mode 100644 index 0000000000..d96950abbc --- /dev/null +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.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 TestBankReconciliationTool(unittest.TestCase): + pass diff --git a/erpnext/accounts/doctype/bank_statement_settings_item/__init__.py b/erpnext/accounts/doctype/bank_statement_import/__init__.py similarity index 100% rename from erpnext/accounts/doctype/bank_statement_settings_item/__init__.py rename to erpnext/accounts/doctype/bank_statement_import/__init__.py diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.css b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.css new file mode 100644 index 0000000000..5206540a33 --- /dev/null +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.css @@ -0,0 +1,3 @@ +.warnings .warning { + margin-bottom: 40px; +} diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js new file mode 100644 index 0000000000..ad4ff9ee60 --- /dev/null +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js @@ -0,0 +1,574 @@ +// Copyright (c) 2019, Frappe Technologies and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Bank Statement Import", { + setup(frm) { + frappe.realtime.on("data_import_refresh", ({ data_import }) => { + frm.import_in_progress = false; + if (data_import !== frm.doc.name) return; + frappe.model.clear_doc("Bank Statement Import", frm.doc.name); + frappe.model + .with_doc("Bank Statement Import", frm.doc.name) + .then(() => { + frm.refresh(); + }); + }); + frappe.realtime.on("data_import_progress", (data) => { + frm.import_in_progress = true; + if (data.data_import !== frm.doc.name) { + return; + } + let percent = Math.floor((data.current * 100) / data.total); + let seconds = Math.floor(data.eta); + let minutes = Math.floor(data.eta / 60); + let eta_message = + // prettier-ignore + seconds < 60 + ? __('About {0} seconds remaining', [seconds]) + : minutes === 1 + ? __('About {0} minute remaining', [minutes]) + : __('About {0} minutes remaining', [minutes]); + + let message; + if (data.success) { + let message_args = [data.current, data.total, eta_message]; + message = + frm.doc.import_type === "Insert New Records" + ? __("Importing {0} of {1}, {2}", message_args) + : __("Updating {0} of {1}, {2}", message_args); + } + if (data.skipping) { + message = __( + "Skipping {0} of {1}, {2}", + [ + data.current, + data.total, + eta_message, + ] + ); + } + frm.dashboard.show_progress( + __("Import Progress"), + percent, + message + ); + frm.page.set_indicator(__("In Progress"), "orange"); + + // hide progress when complete + if (data.current === data.total) { + setTimeout(() => { + frm.dashboard.hide(); + frm.refresh(); + }, 2000); + } + }); + + frm.set_query("reference_doctype", () => { + return { + filters: { + name: ["in", frappe.boot.user.can_import], + }, + }; + }); + + frm.get_field("import_file").df.options = { + restrictions: { + allowed_file_types: [".csv", ".xls", ".xlsx"], + }, + }; + + frm.has_import_file = () => { + return frm.doc.import_file || frm.doc.google_sheets_url; + }; + }, + + refresh(frm) { + frm.page.hide_icon_group(); + frm.trigger("update_indicators"); + frm.trigger("import_file"); + frm.trigger("show_import_log"); + frm.trigger("show_import_warnings"); + frm.trigger("toggle_submit_after_import"); + frm.trigger("show_import_status"); + frm.trigger("show_report_error_button"); + + if (frm.doc.status === "Partial Success") { + frm.add_custom_button(__("Export Errored Rows"), () => + frm.trigger("export_errored_rows") + ); + } + + if (frm.doc.status.includes("Success")) { + frm.add_custom_button( + __("Go to {0} List", [frm.doc.reference_doctype]), + () => frappe.set_route("List", frm.doc.reference_doctype) + ); + } + }, + + onload_post_render(frm) { + frm.trigger("update_primary_action"); + }, + + update_primary_action(frm) { + if (frm.is_dirty()) { + frm.enable_save(); + return; + } + frm.disable_save(); + if (frm.doc.status !== "Success") { + if (!frm.is_new() && frm.has_import_file()) { + let label = + frm.doc.status === "Pending" + ? __("Start Import") + : __("Retry"); + frm.page.set_primary_action(label, () => + frm.events.start_import(frm) + ); + } else { + frm.page.set_primary_action(__("Save"), () => frm.save()); + } + } + }, + + update_indicators(frm) { + const indicator = frappe.get_indicator(frm.doc); + if (indicator) { + frm.page.set_indicator(indicator[0], indicator[1]); + } else { + frm.page.clear_indicator(); + } + }, + + show_import_status(frm) { + let import_log = JSON.parse(frm.doc.import_log || "[]"); + let successful_records = import_log.filter((log) => log.success); + let failed_records = import_log.filter((log) => !log.success); + if (successful_records.length === 0) return; + + let message; + if (failed_records.length === 0) { + let message_args = [successful_records.length]; + if (frm.doc.import_type === "Insert New Records") { + message = + successful_records.length > 1 + ? __("Successfully imported {0} records.", message_args) + : __("Successfully imported {0} record.", message_args); + } else { + message = + successful_records.length > 1 + ? __("Successfully updated {0} records.", message_args) + : __("Successfully updated {0} record.", message_args); + } + } else { + let message_args = [successful_records.length, import_log.length]; + if (frm.doc.import_type === "Insert New Records") { + message = + successful_records.length > 1 + ? __( + "Successfully imported {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.", + message_args + ) + : __( + "Successfully imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.", + message_args + ); + } else { + message = + successful_records.length > 1 + ? __( + "Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.", + message_args + ) + : __( + "Successfully updated {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.", + message_args + ); + } + } + frm.dashboard.set_headline(message); + }, + + show_report_error_button(frm) { + if (frm.doc.status === "Error") { + frappe.db + .get_list("Error Log", { + filters: { method: frm.doc.name }, + fields: ["method", "error"], + order_by: "creation desc", + limit: 1, + }) + .then((result) => { + if (result.length > 0) { + frm.add_custom_button("Report Error", () => { + let fake_xhr = { + responseText: JSON.stringify({ + exc: result[0].error, + }), + }; + frappe.request.report_error(fake_xhr, {}); + }); + } + }); + } + }, + + start_import(frm) { + frm.call({ + method: "form_start_import", + args: { data_import: frm.doc.name }, + btn: frm.page.btn_primary, + }).then((r) => { + if (r.message === true) { + frm.disable_save(); + } + }); + }, + + download_template() { + let method = + "/api/method/frappe.core.doctype.data_import.data_import.download_template"; + + open_url_post(method, { + doctype: "Bank Transaction", + export_records: "5_records", + export_fields: { + "Bank Transaction": [ + "date", + "deposit", + "withdrawal", + "description", + "reference_number", + ], + }, + }); + }, + + reference_doctype(frm) { + frm.trigger("toggle_submit_after_import"); + }, + + toggle_submit_after_import(frm) { + frm.toggle_display("submit_after_import", false); + let doctype = frm.doc.reference_doctype; + if (doctype) { + frappe.model.with_doctype(doctype, () => { + let meta = frappe.get_meta(doctype); + frm.toggle_display("submit_after_import", meta.is_submittable); + }); + } + }, + + google_sheets_url(frm) { + if (!frm.is_dirty()) { + frm.trigger("import_file"); + } else { + frm.trigger("update_primary_action"); + } + }, + + refresh_google_sheet(frm) { + frm.trigger("import_file"); + }, + + import_file(frm) { + frm.toggle_display("section_import_preview", frm.has_import_file()); + if (!frm.has_import_file()) { + frm.get_field("import_preview").$wrapper.empty(); + return; + } else { + frm.trigger("update_primary_action"); + } + + // load import preview + frm.get_field("import_preview").$wrapper.empty(); + $('') + .html(__("Loading import file...")) + .appendTo(frm.get_field("import_preview").$wrapper); + + frm.call({ + method: "get_preview_from_template", + args: { + data_import: frm.doc.name, + import_file: frm.doc.import_file, + google_sheets_url: frm.doc.google_sheets_url, + }, + error_handlers: { + TimestampMismatchError() { + // ignore this error + }, + }, + }).then((r) => { + let preview_data = r.message; + frm.events.show_import_preview(frm, preview_data); + frm.events.show_import_warnings(frm, preview_data); + }); + }, + // method: 'frappe.core.doctype.data_import.data_import.get_preview_from_template', + + show_import_preview(frm, preview_data) { + let import_log = JSON.parse(frm.doc.import_log || "[]"); + + if ( + frm.import_preview && + frm.import_preview.doctype === frm.doc.reference_doctype + ) { + frm.import_preview.preview_data = preview_data; + frm.import_preview.import_log = import_log; + frm.import_preview.refresh(); + return; + } + + frappe.require("/assets/js/data_import_tools.min.js", () => { + frm.import_preview = new frappe.data_import.ImportPreview({ + wrapper: frm.get_field("import_preview").$wrapper, + doctype: frm.doc.reference_doctype, + preview_data, + import_log, + frm, + events: { + remap_column(changed_map) { + let template_options = JSON.parse( + frm.doc.template_options || "{}" + ); + template_options.column_to_field_map = + template_options.column_to_field_map || {}; + Object.assign( + template_options.column_to_field_map, + changed_map + ); + frm.set_value( + "template_options", + JSON.stringify(template_options) + ); + frm.save().then(() => frm.trigger("import_file")); + }, + }, + }); + }); + }, + + export_errored_rows(frm) { + open_url_post( + "/api/method/frappe.core.doctype.data_import.data_import.download_errored_template", + { + data_import_name: frm.doc.name, + } + ); + }, + + show_import_warnings(frm, preview_data) { + let columns = preview_data.columns; + let warnings = JSON.parse(frm.doc.template_warnings || "[]"); + warnings = warnings.concat(preview_data.warnings || []); + + frm.toggle_display("import_warnings_section", warnings.length > 0); + if (warnings.length === 0) { + frm.get_field("import_warnings").$wrapper.html(""); + return; + } + + // group warnings by row + let warnings_by_row = {}; + let other_warnings = []; + for (let warning of warnings) { + if (warning.row) { + warnings_by_row[warning.row] = + warnings_by_row[warning.row] || []; + warnings_by_row[warning.row].push(warning); + } else { + other_warnings.push(warning); + } + } + + let html = ""; + html += Object.keys(warnings_by_row) + .map((row_number) => { + let message = warnings_by_row[row_number] + .map((w) => { + if (w.field) { + let label = + w.field.label + + (w.field.parent !== frm.doc.reference_doctype + ? ` (${w.field.parent})` + : ""); + return `
  • ${label}: ${w.message}
  • `; + } + return `
  • ${w.message}
  • `; + }) + .join(""); + return ` +
    +
    ${__("Row {0}", [row_number])}
    +
      ${message}
    +
    + `; + }) + .join(""); + + html += other_warnings + .map((warning) => { + let header = ""; + if (warning.col) { + let column_number = `${__( + "Column {0}", + [warning.col] + )}`; + let column_header = columns[warning.col].header_title; + header = `${column_number} (${column_header})`; + } + return ` +
    +
    ${header}
    +
    ${warning.message}
    +
    + `; + }) + .join(""); + frm.get_field("import_warnings").$wrapper.html(` +
    +
    ${html}
    +
    + `); + }, + + show_failed_logs(frm) { + frm.trigger("show_import_log"); + }, + + show_import_log(frm) { + let import_log = JSON.parse(frm.doc.import_log || "[]"); + let logs = import_log; + frm.toggle_display("import_log", false); + frm.toggle_display("import_log_section", logs.length > 0); + + if (logs.length === 0) { + frm.get_field("import_log_preview").$wrapper.empty(); + return; + } + + let rows = logs + .map((log) => { + let html = ""; + if (log.success) { + if (frm.doc.import_type === "Insert New Records") { + html = __( + "Successfully imported {0}", [ + `${frappe.utils.get_form_link( + frm.doc.reference_doctype, + log.docname, + true + )}`, + ] + ); + } else { + html = __( + "Successfully updated {0}", [ + `${frappe.utils.get_form_link( + frm.doc.reference_doctype, + log.docname, + true + )}`, + ] + ); + } + } else { + let messages = log.messages + .map(JSON.parse) + .map((m) => { + let title = m.title + ? `${m.title}` + : ""; + let message = m.message + ? `
    ${m.message}
    ` + : ""; + return title + message; + }) + .join(""); + let id = frappe.dom.get_unique_id(); + html = `${messages} + +
    +
    +
    ${log.exception}
    +
    +
    `; + } + let indicator_color = log.success ? "green" : "red"; + let title = log.success ? __("Success") : __("Failure"); + + if (frm.doc.show_failed_logs && log.success) { + return ""; + } + + return ` + ${log.row_indexes.join(", ")} + +
    ${title}
    + + + ${html} + + `; + }) + .join(""); + + if (!rows && frm.doc.show_failed_logs) { + rows = ` + ${__("No failed logs")} + `; + } + + frm.get_field("import_log_preview").$wrapper.html(` + + + + + + + ${rows} +
    ${__("Row Number")}${__("Status")}${__("Message")}
    + `); + }, + + show_missing_link_values(frm, missing_link_values) { + let can_be_created_automatically = missing_link_values.every( + (d) => d.has_one_mandatory_field + ); + + let html = missing_link_values + .map((d) => { + let doctype = d.doctype; + let values = d.missing_values; + return ` +
    ${doctype}
    +
      ${values.map((v) => `
    • ${v}
    • `).join("")}
    + `; + }) + .join(""); + + if (can_be_created_automatically) { + // prettier-ignore + let message = __('There are some linked records which needs to be created before we can import your file. Do you want to create the following missing records automatically?'); + frappe.confirm(message + html, () => { + frm.call("create_missing_link_values", { + missing_link_values, + }).then((r) => { + let records = r.message; + frappe.msgprint(__( + "Created {0} records successfully.", [ + records.length, + ] + )); + }); + }); + } else { + frappe.msgprint( + // prettier-ignore + __('The following records needs to be created before we can import your file.') + html + ); + } + }, +}); diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json new file mode 100644 index 0000000000..5e913cc2aa --- /dev/null +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json @@ -0,0 +1,227 @@ +{ + "actions": [], + "autoname": "format:Bank Statement Import on {creation}", + "beta": 1, + "creation": "2019-08-04 14:16:08.318714", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "bank_account", + "bank", + "column_break_4", + "google_sheets_url", + "refresh_google_sheet", + "html_5", + "import_file", + "download_template", + "status", + "template_options", + "import_warnings_section", + "template_warnings", + "import_warnings", + "section_import_preview", + "import_preview", + "import_log_section", + "import_log", + "show_failed_logs", + "import_log_preview", + "reference_doctype", + "import_type", + "submit_after_import", + "mute_emails" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "bank_account", + "fieldtype": "Link", + "label": "Bank Account", + "options": "Bank Account", + "reqd": 1, + "set_only_once": 1 + }, + { + "depends_on": "eval:doc.bank_account", + "fetch_from": "bank_account.bank", + "fieldname": "bank", + "fieldtype": "Link", + "label": "Bank", + "options": "Bank", + "read_only": 1, + "set_only_once": 1 + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "download_template", + "fieldtype": "Button", + "label": "Download Template" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "import_file", + "fieldtype": "Attach", + "in_list_view": 1, + "label": "Import File" + }, + { + "fieldname": "import_preview", + "fieldtype": "HTML", + "label": "Import Preview" + }, + { + "fieldname": "section_import_preview", + "fieldtype": "Section Break", + "label": "Preview" + }, + { + "fieldname": "template_options", + "fieldtype": "Code", + "hidden": 1, + "label": "Template Options", + "options": "JSON", + "read_only": 1 + }, + { + "fieldname": "import_log", + "fieldtype": "Code", + "label": "Import Log", + "options": "JSON" + }, + { + "fieldname": "import_log_section", + "fieldtype": "Section Break", + "label": "Import Log" + }, + { + "fieldname": "import_log_preview", + "fieldtype": "HTML", + "label": "Import Log Preview" + }, + { + "default": "Pending", + "fieldname": "status", + "fieldtype": "Select", + "hidden": 1, + "label": "Status", + "options": "Pending\nSuccess\nPartial Success\nError", + "read_only": 1 + }, + { + "fieldname": "template_warnings", + "fieldtype": "Code", + "hidden": 1, + "label": "Template Warnings", + "options": "JSON" + }, + { + "fieldname": "import_warnings_section", + "fieldtype": "Section Break", + "label": "Import File Errors and Warnings" + }, + { + "fieldname": "import_warnings", + "fieldtype": "HTML", + "label": "Import Warnings" + }, + { + "default": "0", + "fieldname": "show_failed_logs", + "fieldtype": "Check", + "label": "Show Failed Logs" + }, + { + "depends_on": "eval:!doc.__islocal && !doc.import_file", + "fieldname": "html_5", + "fieldtype": "HTML", + "options": "
    Or
    " + }, + { + "depends_on": "eval:!doc.__islocal && !doc.import_file\n", + "description": "Must be a publicly accessible Google Sheets URL", + "fieldname": "google_sheets_url", + "fieldtype": "Data", + "label": "Import from Google Sheets" + }, + { + "depends_on": "eval:doc.google_sheets_url && !doc.__unsaved", + "fieldname": "refresh_google_sheet", + "fieldtype": "Button", + "label": "Refresh Google Sheet" + }, + { + "default": "Bank Transaction", + "fieldname": "reference_doctype", + "fieldtype": "Link", + "hidden": 1, + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "reqd": 1, + "set_only_once": 1 + }, + { + "default": "Insert New Records", + "fieldname": "import_type", + "fieldtype": "Select", + "hidden": 1, + "in_list_view": 1, + "label": "Import Type", + "options": "\nInsert New Records\nUpdate Existing Records", + "reqd": 1, + "set_only_once": 1 + }, + { + "default": "1", + "fieldname": "submit_after_import", + "fieldtype": "Check", + "hidden": 1, + "label": "Submit After Import", + "set_only_once": 1 + }, + { + "default": "1", + "fieldname": "mute_emails", + "fieldtype": "Check", + "hidden": 1, + "label": "Don't Send Emails", + "set_only_once": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + } + ], + "hide_toolbar": 1, + "links": [], + "modified": "2021-02-10 19:29:59.027325", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Bank Statement Import", + "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/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py new file mode 100644 index 0000000000..9f41b13f4b --- /dev/null +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import csv +import json +import re + +import openpyxl +from openpyxl.styles import Font +from openpyxl.utils import get_column_letter +from six import string_types + +import frappe +from frappe.core.doctype.data_import.importer import Importer, ImportFile +from frappe.utils.background_jobs import enqueue +from frappe.utils.xlsxutils import handle_html, ILLEGAL_CHARACTERS_RE +from frappe import _ + +from frappe.core.doctype.data_import.data_import import DataImport + +class BankStatementImport(DataImport): + def __init__(self, *args, **kwargs): + super(BankStatementImport, self).__init__(*args, **kwargs) + + def validate(self): + doc_before_save = self.get_doc_before_save() + if ( + not (self.import_file or self.google_sheets_url) + or (doc_before_save and doc_before_save.import_file != self.import_file) + or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url) + ): + + template_options_dict = {} + column_to_field_map = {} + bank = frappe.get_doc("Bank", self.bank) + for i in bank.bank_transaction_mapping: + column_to_field_map[i.file_field] = i.bank_transaction_field + template_options_dict["column_to_field_map"] = column_to_field_map + self.template_options = json.dumps(template_options_dict) + + self.template_warnings = "" + + self.validate_import_file() + self.validate_google_sheets_url() + + def start_import(self): + + from frappe.core.page.background_jobs.background_jobs import get_info + from frappe.utils.scheduler import is_scheduler_inactive + + if is_scheduler_inactive() and not frappe.flags.in_test: + frappe.throw( + _("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive") + ) + + enqueued_jobs = [d.get("job_name") for d in get_info()] + + if self.name not in enqueued_jobs: + enqueue( + start_import, + queue="default", + timeout=6000, + event="data_import", + job_name=self.name, + data_import=self.name, + bank_account=self.bank_account, + import_file_path=self.import_file, + bank=self.bank, + template_options=self.template_options, + now=frappe.conf.developer_mode or frappe.flags.in_test, + ) + return True + + return False + +@frappe.whitelist() +def get_preview_from_template(data_import, import_file=None, google_sheets_url=None): + return frappe.get_doc("Bank Statement Import", data_import).get_preview_from_template( + import_file, google_sheets_url + ) + +@frappe.whitelist() +def form_start_import(data_import): + return frappe.get_doc("Bank Statement Import", data_import).start_import() + +@frappe.whitelist() +def download_errored_template(data_import_name): + data_import = frappe.get_doc("Bank Statement Import", data_import_name) + data_import.export_errored_rows() + +def start_import(data_import, bank_account, import_file_path, bank, template_options): + """This method runs in background job""" + + update_mapping_db(bank, template_options) + + data_import = frappe.get_doc("Bank Statement Import", data_import) + + import_file = ImportFile("Bank Transaction", file = import_file_path, import_type="Insert New Records") + data = import_file.raw_data + + add_bank_account(data, bank_account) + write_files(import_file, data) + + try: + i = Importer(data_import.reference_doctype, data_import=data_import) + i.import_data() + except Exception: + frappe.db.rollback() + data_import.db_set("status", "Error") + frappe.log_error(title=data_import.name) + finally: + frappe.flags.in_import = False + + frappe.publish_realtime("data_import_refresh", {"data_import": data_import.name}) + +def update_mapping_db(bank, template_options): + bank = frappe.get_doc("Bank", bank) + for d in bank.bank_transaction_mapping: + d.delete() + + for d in json.loads(template_options)["column_to_field_map"].items(): + bank.append("bank_transaction_mapping", {"bank_transaction_field": d[1] ,"file_field": d[0]} ) + + bank.save() + +def add_bank_account(data, bank_account): + bank_account_loc = None + if "Bank Account" not in data[0]: + data[0].append("Bank Account") + else: + for loc, header in enumerate(data[0]): + if header == "Bank Account": + bank_account_loc = loc + + for row in data[1:]: + if bank_account_loc: + row[bank_account_loc] = bank_account + else: + row.append(bank_account) + +def write_files(import_file, data): + full_file_path = import_file.file_doc.get_full_path() + parts = import_file.file_doc.get_extension() + extension = parts[1] + extension = extension.lstrip(".") + + if extension == "csv": + with open(full_file_path, 'w', newline='') as file: + writer = csv.writer(file) + writer.writerows(data) + elif extension == "xlsx" or "xls": + write_xlsx(data, "trans", file_path = full_file_path) + +def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None): + # from xlsx utils with changes + column_widths = column_widths or [] + if wb is None: + wb = openpyxl.Workbook(write_only=True) + + ws = wb.create_sheet(sheet_name, 0) + + for i, column_width in enumerate(column_widths): + if column_width: + ws.column_dimensions[get_column_letter(i + 1)].width = column_width + + row1 = ws.row_dimensions[1] + row1.font = Font(name='Calibri', bold=True) + + for row in data: + clean_row = [] + for item in row: + if isinstance(item, string_types) and (sheet_name not in ['Data Import Template', 'Data Export']): + value = handle_html(item) + else: + value = item + + if isinstance(item, string_types) and next(ILLEGAL_CHARACTERS_RE.finditer(value), None): + # Remove illegal characters from the string + value = re.sub(ILLEGAL_CHARACTERS_RE, '', value) + + clean_row.append(value) + + ws.append(clean_row) + + wb.save(file_path) + return True + +@frappe.whitelist() +def upload_bank_statement(**args): + args = frappe._dict(args) + bsi = frappe.new_doc("Bank Statement Import") + + if args.company: + bsi.update({ + "company": args.company, + }) + + if args.bank_account: + bsi.update({ + "bank_account": args.bank_account + }) + + return bsi diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js new file mode 100644 index 0000000000..6c754022e6 --- /dev/null +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js @@ -0,0 +1,36 @@ +let imports_in_progress = []; + +frappe.listview_settings['Bank Statement Import'] = { + onload(listview) { + frappe.realtime.on('data_import_progress', data => { + if (!imports_in_progress.includes(data.data_import)) { + imports_in_progress.push(data.data_import); + } + }); + frappe.realtime.on('data_import_refresh', data => { + imports_in_progress = imports_in_progress.filter( + d => d !== data.data_import + ); + listview.refresh(); + }); + }, + get_indicator: function(doc) { + var colors = { + 'Pending': 'orange', + 'Not Started': 'orange', + 'Partial Success': 'orange', + 'Success': 'green', + 'In Progress': 'orange', + 'Error': 'red' + }; + let status = doc.status; + if (imports_in_progress.includes(doc.name)) { + status = 'In Progress'; + } + if (status == 'Pending') { + status = 'Not Started'; + } + return [__(status), colors[status], 'status,=,' + doc.status]; + }, + hide_name_column: true +}; diff --git a/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py new file mode 100644 index 0000000000..cd5831412d --- /dev/null +++ b/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestBankStatementImport(unittest.TestCase): + pass diff --git a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.js b/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.js deleted file mode 100644 index 46aa4f2031..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2017, sathishpy@gmail.com and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Bank Statement Settings', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.json b/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.json deleted file mode 100644 index 53fbf7d446..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.json +++ /dev/null @@ -1,272 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "beta": 0, - "creation": "2017-11-13 13:38:10.863592", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Bank Account", - "length": 0, - "no_copy": 0, - "options": "Bank", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "'%d/%m/%Y'", - "fieldname": "date_format", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date Format", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "statement_header_mapping", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Statement Header Mapping", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "header_items", - "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": "Statement Headers", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Settings Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transaction_data_mapping", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transaction Data Mapping", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mapped_items", - "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": "Mapped Items", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Transaction Settings Item", - "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 - } - ], - "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-04-07 18:57:04.048423", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Settings", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 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, - "apply_user_permissions": 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": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.py b/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.py deleted file mode 100644 index 6c4dd1b85b..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document - -class BankStatementSettings(Document): - def autoname(self): - self.name = self.bank + "-Statement-Settings" diff --git a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.js b/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.js deleted file mode 100644 index f2381c042e..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Bank Statement Settings", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Bank Statement Settings - () => frappe.tests.make('Bank Statement Settings', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.py b/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.py deleted file mode 100644 index aa7fe83328..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and Contributors -# See license.txt -from __future__ import unicode_literals - -import frappe -import unittest - -class TestBankStatementSettings(unittest.TestCase): - pass diff --git a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.json b/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.json deleted file mode 100644 index 7c93f268f5..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-01-08 00:16:42.762980", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mapped_header", - "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": "Mapped Header", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stmt_header", - "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": "Bank Header", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-01-08 00:19:14.841134", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Settings Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.py b/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.py deleted file mode 100644 index 9438e9a63f..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, sathishpy@gmail.com and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document - -class BankStatementSettingsItem(Document): - pass diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.js b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.js deleted file mode 100644 index 736ed35ae1..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.js +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2017, sathishpy@gmail.com and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Bank Statement Transaction Entry', { - setup: function(frm) { - frm.events.account_filters(frm) - frm.events.invoice_filter(frm) - }, - refresh: function(frm) { - frm.set_df_property("bank_account", "read_only", frm.doc.__islocal ? 0 : 1); - frm.set_df_property("from_date", "read_only", frm.doc.__islocal ? 0 : 1); - frm.set_df_property("to_date", "read_only", frm.doc.__islocal ? 0 : 1); - }, - invoke_doc_function(frm, method) { - frappe.call({ - doc: frm.doc, - method: method, - callback: function(r) { - if(!r.exe) { - frm.refresh_fields(); - } - } - }); - }, - account_filters: function(frm) { - frm.fields_dict['bank_account'].get_query = function(doc, dt, dn) { - return { - filters:[ - ["Account", "account_type", "in", ["Bank"]] - ] - } - }; - frm.fields_dict['receivable_account'].get_query = function(doc, dt, dn) { - return { - filters: {"account_type": "Receivable"} - } - }; - frm.fields_dict['payable_account'].get_query = function(doc, dt, dn) { - return { - filters: {"account_type": "Payable"} - } - }; - }, - - invoice_filter: function(frm) { - frm.set_query("invoice", "payment_invoice_items", function(doc, cdt, cdn) { - let row = locals[cdt][cdn] - if (row.party_type == "Customer") { - return { - filters:[[row.invoice_type, "customer", "in", [row.party]], - [row.invoice_type, "status", "!=", "Cancelled" ], - [row.invoice_type, "posting_date", "<", row.transaction_date ], - [row.invoice_type, "outstanding_amount", ">", 0 ]] - } - } else if (row.party_type == "Supplier") { - return { - filters:[[row.invoice_type, "supplier", "in", [row.party]], - [row.invoice_type, "status", "!=", "Cancelled" ], - [row.invoice_type, "posting_date", "<", row.transaction_date ], - [row.invoice_type, "outstanding_amount", ">", 0 ]] - } - } - }); - }, - - match_invoices: function(frm) { - frm.events.invoke_doc_function(frm, "populate_matching_invoices"); - }, - create_payments: function(frm) { - frm.events.invoke_doc_function(frm, "create_payment_entries"); - }, - submit_payments: function(frm) { - frm.events.invoke_doc_function(frm, "submit_payment_entries"); - }, -}); - - -frappe.ui.form.on('Bank Statement Transaction Invoice Item', { - party_type: function(frm, cdt, cdn) { - let row = locals[cdt][cdn]; - if (row.party_type == "Customer") { - row.invoice_type = "Sales Invoice"; - } else if (row.party_type == "Supplier") { - row.invoice_type = "Purchase Invoice"; - } else if (row.party_type == "Account") { - row.invoice_type = "Journal Entry"; - } - refresh_field("invoice_type", row.name, "payment_invoice_items"); - - }, - invoice_type: function(frm, cdt, cdn) { - let row = locals[cdt][cdn]; - if (row.invoice_type == "Purchase Invoice") { - row.party_type = "Supplier"; - } else if (row.invoice_type == "Sales Invoice") { - row.party_type = "Customer"; - } - refresh_field("party_type", row.name, "payment_invoice_items"); - } -}); \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.json b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.json deleted file mode 100644 index fb80169c37..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.json +++ /dev/null @@ -1,792 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "beta": 0, - "creation": "2017-11-07 13:48:13.123185", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Bank Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "from_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": "From Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "to_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": "To Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_settings", - "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": "Bank Statement Settings", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Settings", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank", - "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": "Bank", - "length": 0, - "no_copy": 0, - "options": "Bank", - "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": "receivable_account", - "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": "Receivable Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payable_account", - "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": "Payable Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_statement", - "fieldtype": "Attach", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Statement", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Transaction Entries", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "new_transaction_items", - "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": "New Transactions", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Transaction Payment Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.new_transaction_items && doc.new_transaction_items.length", - "fieldname": "section_break_9", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "match_invoices", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Match Transaction to Invoices", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_14", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "create_payments", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Create New Payment/Journal Entry", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_16", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "submit_payments", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Submit/Reconcile Payments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.new_transaction_items && doc.new_transaction_items.length", - "fieldname": "section_break_18", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Matching Invoices", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_invoice_items", - "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": "Payment Invoice Items", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Transaction Invoice Item", - "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": "reconciled_transactions", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reconciled Transactions", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reconciled_transaction_items", - "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": "Reconciled Transactions", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Transaction Payment Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Bank Statement Transaction Entry", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-09-14 18:04:44.170455", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Transaction Entry", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py deleted file mode 100644 index 27dd8e463f..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py +++ /dev/null @@ -1,443 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.model.document import Document -from erpnext.accounts.utils import get_outstanding_invoices -from frappe.utils import nowdate -from datetime import datetime -import csv, os, re, io -import difflib -import copy - -class BankStatementTransactionEntry(Document): - def autoname(self): - self.name = self.bank_account + "-" + self.from_date + "-" + self.to_date - if self.bank: - mapper_name = self.bank + "-Statement-Settings" - if not frappe.db.exists("Bank Statement Settings", mapper_name): - self.create_settings(self.bank) - self.bank_settings = mapper_name - - def create_settings(self, bank): - mapper = frappe.new_doc("Bank Statement Settings") - mapper.bank = bank - mapper.date_format = "%Y-%m-%d" - mapper.bank_account = self.bank_account - for header in ["Date", "Particulars", "Withdrawals", "Deposits", "Balance"]: - header_item = mapper.append("header_items", {}) - header_item.mapped_header = header_item.stmt_header = header - mapper.save() - - def on_update(self): - if (not self.bank_statement): - self.reconciled_transaction_items = self.new_transaction_items = [] - return - - if len(self.new_transaction_items + self.reconciled_transaction_items) == 0: - self.populate_payment_entries() - else: - self.match_invoice_to_payment() - - def validate(self): - if not self.new_transaction_items: - self.populate_payment_entries() - - def get_statement_headers(self): - if not self.bank_settings: - frappe.throw(_("Bank Data mapper doesn't exist")) - mapper_doc = frappe.get_doc("Bank Statement Settings", self.bank_settings) - headers = {entry.mapped_header:entry.stmt_header for entry in mapper_doc.header_items} - return headers - - def populate_payment_entries(self): - if self.bank_statement is None: return - file_url = self.bank_statement - if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0): - frappe.throw(_("Transactions already retreived from the statement")) - - date_format = frappe.get_value("Bank Statement Settings", self.bank_settings, "date_format") - if (date_format is None): - date_format = '%Y-%m-%d' - if self.bank_settings: - mapped_items = frappe.get_doc("Bank Statement Settings", self.bank_settings).mapped_items - statement_headers = self.get_statement_headers() - transactions = get_transaction_entries(file_url, statement_headers) - for entry in transactions: - date = entry[statement_headers["Date"]].strip() - #print("Processing entry DESC:{0}-W:{1}-D:{2}-DT:{3}".format(entry["Particulars"], entry["Withdrawals"], entry["Deposits"], entry["Date"])) - if (not date): continue - transaction_date = datetime.strptime(date, date_format).date() - if (self.from_date and transaction_date < datetime.strptime(self.from_date, '%Y-%m-%d').date()): continue - if (self.to_date and transaction_date > datetime.strptime(self.to_date, '%Y-%m-%d').date()): continue - bank_entry = self.append('new_transaction_items', {}) - bank_entry.transaction_date = transaction_date - bank_entry.description = entry[statement_headers["Particulars"]] - - mapped_item = next((entry for entry in mapped_items if entry.mapping_type == "Transaction" and frappe.safe_decode(entry.bank_data.lower()) in frappe.safe_decode(bank_entry.description.lower())), None) - if (mapped_item is not None): - bank_entry.party_type = mapped_item.mapped_data_type - bank_entry.party = mapped_item.mapped_data - else: - bank_entry.party_type = "Supplier" if not entry[statement_headers["Deposits"]].strip() else "Customer" - party_list = frappe.get_all(bank_entry.party_type, fields=["name"]) - parties = [party.name for party in party_list] - matches = difflib.get_close_matches(frappe.safe_decode(bank_entry.description.lower()), parties, 1, 0.4) - if len(matches) > 0: bank_entry.party = matches[0] - bank_entry.amount = -float(entry[statement_headers["Withdrawals"]]) if not entry[statement_headers["Deposits"]].strip() else float(entry[statement_headers["Deposits"]]) - self.map_unknown_transactions() - self.map_transactions_on_journal_entry() - - def map_transactions_on_journal_entry(self): - for entry in self.new_transaction_items: - vouchers = frappe.db.sql("""select name, posting_date from `tabJournal Entry` - where posting_date='{0}' and total_credit={1} and cheque_no='{2}' and docstatus != 2 - """.format(entry.transaction_date, abs(entry.amount), frappe.safe_decode(entry.description)), as_dict=True) - if (len(vouchers) == 1): - entry.reference_name = vouchers[0].name - - def populate_matching_invoices(self): - self.payment_invoice_items = [] - self.map_unknown_transactions() - added_invoices = [] - for entry in self.new_transaction_items: - if (not entry.party or entry.party_type == "Account"): continue - account = self.receivable_account if entry.party_type == "Customer" else self.payable_account - invoices = get_outstanding_invoices(entry.party_type, entry.party, account) - transaction_date = datetime.strptime(entry.transaction_date, "%Y-%m-%d").date() - outstanding_invoices = [invoice for invoice in invoices if invoice.posting_date <= transaction_date] - amount = abs(entry.amount) - matching_invoices = [invoice for invoice in outstanding_invoices if invoice.outstanding_amount == amount] - sorted(outstanding_invoices, key=lambda k: k['posting_date']) - for e in (matching_invoices + outstanding_invoices): - added = next((inv for inv in added_invoices if inv == e.get('voucher_no')), None) - if (added is not None): continue - ent = self.append('payment_invoice_items', {}) - ent.transaction_date = entry.transaction_date - ent.payment_description = frappe.safe_decode(entry.description) - ent.party_type = entry.party_type - ent.party = entry.party - ent.invoice = e.get('voucher_no') - added_invoices += [ent.invoice] - ent.invoice_type = "Sales Invoice" if entry.party_type == "Customer" else "Purchase Invoice" - ent.invoice_date = e.get('posting_date') - ent.outstanding_amount = e.get('outstanding_amount') - ent.allocated_amount = min(float(e.get('outstanding_amount')), amount) - amount -= float(e.get('outstanding_amount')) - if (amount <= 5): break - self.match_invoice_to_payment() - self.populate_matching_vouchers() - self.map_transactions_on_journal_entry() - - def match_invoice_to_payment(self): - added_payments = [] - for entry in self.new_transaction_items: - if (not entry.party or entry.party_type == "Account"): continue - entry.account = self.receivable_account if entry.party_type == "Customer" else self.payable_account - amount = abs(entry.amount) - payment, matching_invoices = None, [] - for inv_entry in self.payment_invoice_items: - if (inv_entry.payment_description != frappe.safe_decode(entry.description) or inv_entry.transaction_date != entry.transaction_date): continue - if (inv_entry.party != entry.party): continue - matching_invoices += [inv_entry.invoice_type + "|" + inv_entry.invoice] - payment = get_payments_matching_invoice(inv_entry.invoice, entry.amount, entry.transaction_date) - doc = frappe.get_doc(inv_entry.invoice_type, inv_entry.invoice) - inv_entry.invoice_date = doc.posting_date - inv_entry.outstanding_amount = doc.outstanding_amount - inv_entry.allocated_amount = min(float(doc.outstanding_amount), amount) - amount -= inv_entry.allocated_amount - if (amount < 0): break - - amount = abs(entry.amount) - if (payment is None): - order_doctype = "Sales Order" if entry.party_type=="Customer" else "Purchase Order" - from erpnext.controllers.accounts_controller import get_advance_payment_entries - payment_entries = get_advance_payment_entries(entry.party_type, entry.party, entry.account, order_doctype, against_all_orders=True) - payment_entries += self.get_matching_payments(entry.party, amount, entry.transaction_date) - payment = next((payment for payment in payment_entries if payment.amount == amount and payment not in added_payments), None) - if (payment is None): - print("Failed to find payments for {0}:{1}".format(entry.party, amount)) - continue - added_payments += [payment] - entry.reference_type = payment.reference_type - entry.reference_name = payment.reference_name - entry.mode_of_payment = "Wire Transfer" - entry.outstanding_amount = min(amount, 0) - if (entry.payment_reference is None): - entry.payment_reference = frappe.safe_decode(entry.description) - entry.invoices = ",".join(matching_invoices) - #print("Matching payment is {0}:{1}".format(entry.reference_type, entry.reference_name)) - - def get_matching_payments(self, party, amount, pay_date): - query = """select 'Payment Entry' as reference_type, name as reference_name, paid_amount as amount - from `tabPayment Entry` where party='{0}' and paid_amount={1} and posting_date='{2}' and docstatus != 2 - """.format(party, amount, pay_date) - matching_payments = frappe.db.sql(query, as_dict=True) - return matching_payments - - def map_unknown_transactions(self): - for entry in self.new_transaction_items: - if (entry.party): continue - inv_type = "Sales Invoice" if (entry.amount > 0) else "Purchase Invoice" - party_type = "customer" if (entry.amount > 0) else "supplier" - - query = """select posting_date, name, {0}, outstanding_amount - from `tab{1}` where ROUND(outstanding_amount)={2} and posting_date < '{3}' - """.format(party_type, inv_type, round(abs(entry.amount)), entry.transaction_date) - invoices = frappe.db.sql(query, as_dict = True) - if(len(invoices) > 0): - entry.party = invoices[0].get(party_type) - - def populate_matching_vouchers(self): - for entry in self.new_transaction_items: - if (not entry.party or entry.reference_name): continue - print("Finding matching voucher for {0}".format(frappe.safe_decode(entry.description))) - amount = abs(entry.amount) - invoices = [] - vouchers = get_matching_journal_entries(self.from_date, self.to_date, entry.party, self.bank_account, amount) - if len(vouchers) == 0: continue - for voucher in vouchers: - added = next((entry.invoice for entry in self.payment_invoice_items if entry.invoice == voucher.voucher_no), None) - if (added): - print("Found voucher {0}".format(added)) - continue - print("Adding voucher {0} {1} {2}".format(voucher.voucher_no, voucher.posting_date, voucher.debit)) - ent = self.append('payment_invoice_items', {}) - ent.invoice_date = voucher.posting_date - ent.invoice_type = "Journal Entry" - ent.invoice = voucher.voucher_no - ent.payment_description = frappe.safe_decode(entry.description) - ent.allocated_amount = max(voucher.debit, voucher.credit) - - invoices += [ent.invoice_type + "|" + ent.invoice] - entry.reference_type = "Journal Entry" - entry.mode_of_payment = "Wire Transfer" - entry.reference_name = ent.invoice - #entry.account = entry.party - entry.invoices = ",".join(invoices) - break - - - def create_payment_entries(self): - for payment_entry in self.new_transaction_items: - if (not payment_entry.party): continue - if (payment_entry.reference_name): continue - print("Creating payment entry for {0}".format(frappe.safe_decode(payment_entry.description))) - if (payment_entry.party_type == "Account"): - payment = self.create_journal_entry(payment_entry) - invoices = [payment.doctype + "|" + payment.name] - payment_entry.invoices = ",".join(invoices) - else: - payment = self.create_payment_entry(payment_entry) - invoices = [entry.reference_doctype + "|" + entry.reference_name for entry in payment.references if entry is not None] - payment_entry.invoices = ",".join(invoices) - payment_entry.mode_of_payment = payment.mode_of_payment - payment_entry.account = self.receivable_account if payment_entry.party_type == "Customer" else self.payable_account - payment_entry.reference_name = payment.name - payment_entry.reference_type = payment.doctype - frappe.msgprint(_("Successfully created payment entries")) - - def create_payment_entry(self, pe): - payment = frappe.new_doc("Payment Entry") - payment.posting_date = pe.transaction_date - payment.payment_type = "Receive" if pe.party_type == "Customer" else "Pay" - payment.mode_of_payment = "Wire Transfer" - payment.party_type = pe.party_type - payment.party = pe.party - payment.paid_to = self.bank_account if pe.party_type == "Customer" else self.payable_account - payment.paid_from = self.receivable_account if pe.party_type == "Customer" else self.bank_account - payment.paid_amount = payment.received_amount = abs(pe.amount) - payment.reference_no = pe.description - payment.reference_date = pe.transaction_date - payment.save() - for inv_entry in self.payment_invoice_items: - if (pe.description != inv_entry.payment_description or pe.transaction_date != inv_entry.transaction_date): continue - if (pe.party != inv_entry.party): continue - reference = payment.append("references", {}) - reference.reference_doctype = inv_entry.invoice_type - reference.reference_name = inv_entry.invoice - reference.allocated_amount = inv_entry.allocated_amount - print ("Adding invoice {0} {1}".format(reference.reference_name, reference.allocated_amount)) - payment.setup_party_account_field() - payment.set_missing_values() - #payment.set_exchange_rate() - #payment.set_amounts() - #print("Created payment entry {0}".format(payment.as_dict())) - payment.save() - return payment - - def create_journal_entry(self, pe): - je = frappe.new_doc("Journal Entry") - je.is_opening = "No" - je.voucher_type = "Bank Entry" - je.cheque_no = pe.description - je.cheque_date = pe.transaction_date - je.remark = pe.description - je.posting_date = pe.transaction_date - if (pe.amount < 0): - je.append("accounts", {"account": pe.party, "debit_in_account_currency": abs(pe.amount)}) - je.append("accounts", {"account": self.bank_account, "credit_in_account_currency": abs(pe.amount)}) - else: - je.append("accounts", {"account": pe.party, "credit_in_account_currency": pe.amount}) - je.append("accounts", {"account": self.bank_account, "debit_in_account_currency": pe.amount}) - je.save() - return je - - def update_payment_entry(self, payment): - lst = [] - invoices = payment.invoices.strip().split(',') - if (len(invoices) == 0): return - amount = float(abs(payment.amount)) - for invoice_entry in invoices: - if (not invoice_entry.strip()): continue - invs = invoice_entry.split('|') - invoice_type, invoice = invs[0], invs[1] - outstanding_amount = frappe.get_value(invoice_type, invoice, 'outstanding_amount') - - lst.append(frappe._dict({ - 'voucher_type': payment.reference_type, - 'voucher_no' : payment.reference_name, - 'against_voucher_type' : invoice_type, - 'against_voucher' : invoice, - 'account' : payment.account, - 'party_type': payment.party_type, - 'party': frappe.get_value("Payment Entry", payment.reference_name, "party"), - 'unadjusted_amount' : float(amount), - 'allocated_amount' : min(outstanding_amount, amount) - })) - amount -= outstanding_amount - if lst: - from erpnext.accounts.utils import reconcile_against_document - try: - reconcile_against_document(lst) - except: - frappe.throw(_("Exception occurred while reconciling {0}").format(payment.reference_name)) - - def submit_payment_entries(self): - for payment in self.new_transaction_items: - if payment.reference_name is None: continue - doc = frappe.get_doc(payment.reference_type, payment.reference_name) - if doc.docstatus == 1: - if (payment.reference_type == "Journal Entry"): continue - if doc.unallocated_amount == 0: continue - print("Reconciling payment {0}".format(payment.reference_name)) - self.update_payment_entry(payment) - else: - print("Submitting payment {0}".format(payment.reference_name)) - if (payment.reference_type == "Payment Entry"): - if (payment.payment_reference): - doc.reference_no = payment.payment_reference - doc.mode_of_payment = payment.mode_of_payment - doc.save() - doc.submit() - self.move_reconciled_entries() - self.populate_matching_invoices() - - def move_reconciled_entries(self): - idx = 0 - while idx < len(self.new_transaction_items): - entry = self.new_transaction_items[idx] - try: - print("Checking transaction {0}: {2} in {1} entries".format(idx, len(self.new_transaction_items), frappe.safe_decode(entry.description))) - except UnicodeEncodeError: - pass - idx += 1 - if entry.reference_name is None: continue - doc = frappe.get_doc(entry.reference_type, entry.reference_name) - if doc.docstatus == 1 and (entry.reference_type == "Journal Entry" or doc.unallocated_amount == 0): - self.remove(entry) - rc_entry = self.append('reconciled_transaction_items', {}) - dentry = entry.as_dict() - dentry.pop('idx', None) - rc_entry.update(dentry) - idx -= 1 - - -def get_matching_journal_entries(from_date, to_date, account, against, amount): - query = """select voucher_no, posting_date, account, against, debit_in_account_currency as debit, credit_in_account_currency as credit - from `tabGL Entry` - where posting_date between '{0}' and '{1}' and account = '{2}' and against = '{3}' and debit = '{4}' - """.format(from_date, to_date, account, against, amount) - jv_entries = frappe.db.sql(query, as_dict=True) - #print("voucher query:{0}\n Returned {1} entries".format(query, len(jv_entries))) - return jv_entries - -def get_payments_matching_invoice(invoice, amount, pay_date): - query = """select pe.name as reference_name, per.reference_doctype as reference_type, per.outstanding_amount, per.allocated_amount - from `tabPayment Entry Reference` as per JOIN `tabPayment Entry` as pe on pe.name = per.parent - where per.reference_name='{0}' and (posting_date='{1}' or reference_date='{1}') and pe.docstatus != 2 - """.format(invoice, pay_date) - payments = frappe.db.sql(query, as_dict=True) - if (len(payments) == 0): return - payment = next((payment for payment in payments if payment.allocated_amount == amount), payments[0]) - #Hack: Update the reference type which is set to invoice type - payment.reference_type = "Payment Entry" - return payment - -def is_headers_present(headers, row): - for header in headers: - if header not in row: - return False - return True - -def get_header_index(headers, row): - header_index = {} - for header in headers: - if header in row: - header_index[header] = row.index(header) - return header_index - -def get_transaction_info(headers, header_index, row): - transaction = {} - for header in headers: - transaction[header] = row[header_index[header]] - if (transaction[header] == None): - transaction[header] = "" - return transaction - -def get_transaction_entries(file_url, headers): - header_index = {} - rows, transactions = [], [] - - if (file_url.lower().endswith("xlsx")): - from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file - rows = read_xlsx_file_from_attached_file(file_url=file_url) - elif (file_url.lower().endswith("csv")): - from frappe.utils.csvutils import read_csv_content - _file = frappe.get_doc("File", {"file_url": file_url}) - filepath = _file.get_full_path() - with open(filepath,'rb') as csvfile: - rows = read_csv_content(csvfile.read()) - elif (file_url.lower().endswith("xls")): - filename = file_url.split("/")[-1] - rows = get_rows_from_xls_file(filename) - else: - frappe.throw(_("Only .csv and .xlsx files are supported currently")) - - stmt_headers = headers.values() - for row in rows: - if len(row) == 0 or row[0] == None or not row[0]: continue - #print("Processing row {0}".format(row)) - if header_index: - transaction = get_transaction_info(stmt_headers, header_index, row) - transactions.append(transaction) - elif is_headers_present(stmt_headers, row): - header_index = get_header_index(stmt_headers, row) - return transactions - -def get_rows_from_xls_file(filename): - _file = frappe.get_doc("File", {"file_name": filename}) - filepath = _file.get_full_path() - import xlrd - book = xlrd.open_workbook(filepath) - sheets = book.sheets() - rows = [] - for row in range(1, sheets[0].nrows): - row_values = [] - for col in range(1, sheets[0].ncols): - row_values.append(sheets[0].cell_value(row, col)) - rows.append(row_values) - return rows diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.js b/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.js deleted file mode 100644 index 46d570f515..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Bank Statement Transaction Entry", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Bank Statement Transaction Entry - () => frappe.tests.make('Bank Statement Transaction Entry', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.py deleted file mode 100644 index 458948372f..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and Contributors -# See license.txt -from __future__ import unicode_literals - -import frappe -import unittest - -class TestBankStatementTransactionEntry(unittest.TestCase): - pass diff --git a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.json b/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.json deleted file mode 100644 index d96c94d8ca..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.json +++ /dev/null @@ -1,365 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-07 13:58:53.827058", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transaction_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transaction Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 4, - "fieldname": "payment_description", - "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": "Payment Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "party_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Party Type", - "length": 0, - "no_copy": 0, - "options": "Customer\nSupplier\nAccount", - "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": "party", - "fieldtype": "Dynamic 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": "Party", - "length": 0, - "no_copy": 0, - "options": "party_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "invoice_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": "Invoice Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "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": "invoice_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Invoice Type", - "length": 0, - "no_copy": 0, - "options": "Sales Invoice\nPurchase Invoice\nJournal Entry", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "invoice", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "invoice", - "length": 0, - "no_copy": 0, - "options": "invoice_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 1, - "fieldname": "outstanding_amount", - "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": "Outstanding Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 1, - "fieldname": "allocated_amount", - "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": "Allocated Amount", - "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 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-09-14 19:03:30.949831", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Transaction Invoice Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.py b/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.py deleted file mode 100644 index cb1b15815f..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document - -class BankStatementTransactionInvoiceItem(Document): - pass diff --git a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_payment_item/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.json b/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.json deleted file mode 100644 index 177dccd82c..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.json +++ /dev/null @@ -1,494 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-07 14:03:05.651413", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 1, - "fieldname": "transaction_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": "Transaction Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 4, - "fieldname": "description", - "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": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 1, - "fieldname": "amount", - "fieldtype": "Currency", - "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": "Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 1, - "fieldname": "party_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Party Type", - "length": 0, - "no_copy": 0, - "options": "Customer\nSupplier\nAccount", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "party", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Party", - "length": 0, - "no_copy": 0, - "options": "party_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Type", - "length": 0, - "no_copy": 0, - "options": "Payment Entry\nJournal Entry", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "account", - "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": "Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mode_of_payment", - "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": "Mode of Payment", - "length": 0, - "no_copy": 0, - "options": "Mode of Payment", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "outstanding_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "outstanding_amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_10", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "reference_name", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reference Name", - "length": 0, - "no_copy": 0, - "options": "reference_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_reference", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Reference", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "invoices", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Invoices", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2017-11-15 19:18:52.876221", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Transaction Payment Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} diff --git a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.py b/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.py deleted file mode 100644 index 9840c0dbe3..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document - -class BankStatementTransactionPaymentItem(Document): - pass diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_settings/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.js b/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.js deleted file mode 100644 index 46aa4f2031..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2017, sathishpy@gmail.com and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Bank Statement Settings', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.json b/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.json deleted file mode 100644 index 474bb90db7..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.json +++ /dev/null @@ -1,266 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "beta": 0, - "creation": "2017-11-13 13:38:10.863592", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Bank Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "'%d/%m/%Y'", - "fieldname": "date_format", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date Format", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "statement_header_mapping", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Statement Header Mapping", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "header_items", - "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": "Statement Headers", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Settings Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transaction_data_mapping", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transaction Data Mapping", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mapped_items", - "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": "Mapped Items", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Transaction Settings Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "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-01-12 10:34:32.840487", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Settings", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 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, - "apply_user_permissions": 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": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.py b/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.py deleted file mode 100644 index de9a85fe5c..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document - -class BankStatementSettings(Document): - def autoname(self): - self.name = self.bank_account + "-Mappings" diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.js b/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.js deleted file mode 100644 index f2381c042e..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Bank Statement Settings", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Bank Statement Settings - () => frappe.tests.make('Bank Statement Settings', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.py b/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.py deleted file mode 100644 index aa7fe83328..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and Contributors -# See license.txt -from __future__ import unicode_literals - -import frappe -import unittest - -class TestBankStatementSettings(unittest.TestCase): - pass diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_settings_item/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.json b/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.json deleted file mode 100644 index 47c32097a9..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-13 13:42:00.335432", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Transaction", - "fieldname": "mapping_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Mapping Type", - "length": 0, - "no_copy": 0, - "options": "Transaction", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_data", - "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": "Bank Data", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Account", - "fieldname": "mapped_data_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Mapped Data Type", - "length": 0, - "no_copy": 0, - "options": "Account\nCustomer\nSupplier\nAccount", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mapped_data", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Mapped Data", - "length": 0, - "no_copy": 0, - "options": "mapped_data_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-01-08 00:13:49.973501", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Transaction Settings Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.py b/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.py deleted file mode 100644 index bf0a590d48..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document - -class BankStatementTransactionSettingsItem(Document): - pass diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.js b/erpnext/accounts/doctype/bank_transaction/bank_transaction.js index 8b1bab1618..3758b524da 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.js +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.js @@ -1,32 +1,70 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Bank Transaction', { +frappe.ui.form.on("Bank Transaction", { onload(frm) { - frm.set_query('payment_document', 'payment_entries', function() { + frm.set_query("payment_document", "payment_entries", function () { return { - "filters": { - "name": ["in", ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Expense Claim"]] - } + filters: { + name: [ + "in", + [ + "Payment Entry", + "Journal Entry", + "Sales Invoice", + "Purchase Invoice", + "Expense Claim", + ], + ], + }, }; }); - } + }, + bank_account: function (frm) { + set_bank_statement_filter(frm); + }, + + setup: function (frm) { + frm.set_query("party_type", function () { + return { + filters: { + name: ["in", Object.keys(frappe.boot.party_account_types)], + }, + }; + }); + }, }); -frappe.ui.form.on('Bank Transaction Payments', { - payment_entries_remove: function(frm, cdt, cdn) { +frappe.ui.form.on("Bank Transaction Payments", { + payment_entries_remove: function (frm, cdt, cdn) { update_clearance_date(frm, cdt, cdn); - } + }, }); const update_clearance_date = (frm, cdt, cdn) => { if (frm.doc.docstatus === 1) { - frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment', - {doctype: cdt, docname: cdn}) - .then(e => { + frappe + .xcall( + "erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment", + { doctype: cdt, docname: cdn } + ) + .then((e) => { if (e == "success") { - frappe.show_alert({message:__("Document {0} successfully uncleared", [e]), indicator:'green'}); + frappe.show_alert({ + message: __("Document {0} successfully uncleared", [e]), + indicator: "green", + }); } }); } -}; \ No newline at end of file +}; + +function set_bank_statement_filter(frm) { + frm.set_query("bank_statement", function () { + return { + filters: { + bank_account: frm.doc.bank_account, + }, + }; + }); +} diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json index 39937bb364..69ee4971cd 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json @@ -1,833 +1,245 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, - "allow_rename": 0, "autoname": "naming_series:", - "beta": 0, "creation": "2018-10-22 18:19:02.784533", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "naming_series", + "date", + "column_break_2", + "status", + "bank_account", + "company", + "section_break_4", + "deposit", + "withdrawal", + "column_break_7", + "currency", + "section_break_10", + "description", + "section_break_14", + "reference_number", + "transaction_id", + "payment_entries", + "section_break_18", + "allocated_amount", + "amended_from", + "column_break_17", + "unallocated_amount", + "party_section", + "party_type", + "party" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "ACC-BTN-.YYYY.-", - "fetch_if_empty": 0, "fieldname": "naming_series", "fieldtype": "Select", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Series", - "length": 0, "no_copy": 1, "options": "ACC-BTN-.YYYY.-", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Pending", - "fetch_if_empty": 0, "fieldname": "status", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Status", - "length": 0, - "no_copy": 0, - "options": "\nPending\nSettled\nUnreconciled\nReconciled", - "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": "\nPending\nSettled\nUnreconciled\nReconciled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "bank_account", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Bank Account", - "length": 0, - "no_copy": 0, - "options": "Bank Account", - "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": "Bank Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", "fetch_from": "bank_account.company", - "fetch_if_empty": 0, "fieldname": "company", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Company", - "length": 0, - "no_copy": 0, "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "debit", - "fieldtype": "Currency", - "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": "Debit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "credit", - "fieldtype": "Currency", - "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": "Credit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_7", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "currency", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "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": "Currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "section_break_10", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "description", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Description" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "section_break_14", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "allow_on_submit": 1, "fieldname": "reference_number", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Number", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Reference Number" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "transaction_id", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Transaction ID", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, "unique": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "payment_entries", "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": "Payment Entries", - "length": 0, - "no_copy": 0, - "options": "Bank Transaction Payments", - "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": "Bank Transaction Payments" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "section_break_18", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "allocated_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allocated Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Allocated Amount" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Amended From", - "length": 0, "no_copy": 1, "options": "Bank Transaction", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_17", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "unallocated_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Unallocated Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Unallocated Amount" + }, + { + "fieldname": "party_section", + "fieldtype": "Section Break", + "label": "Payment From / To" + }, + { + "allow_on_submit": 1, + "fieldname": "party_type", + "fieldtype": "Link", + "label": "Party Type", + "options": "DocType" + }, + { + "allow_on_submit": 1, + "fieldname": "party", + "fieldtype": "Dynamic Link", + "label": "Party", + "options": "party_type" + }, + { + "fieldname": "deposit", + "oldfieldname": "debit", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Deposit" + }, + { + "fieldname": "withdrawal", + "oldfieldname": "credit", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Withdrawal" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-05-11 05:27:55.244721", + "links": [], + "modified": "2020-12-30 19:40:54.221070", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Transaction", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, "cancel": 1, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 }, { - "amend": 0, "cancel": 1, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Accounts Manager", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Accounts User", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, "sort_field": "date", "sort_order": "DESC", "title_field": "bank_account", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 0e45db3dbc..5246baa02b 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -11,7 +11,7 @@ from frappe import _ class BankTransaction(StatusUpdater): def after_insert(self): - self.unallocated_amount = abs(flt(self.credit) - flt(self.debit)) + self.unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit)) def on_submit(self): self.clear_linked_payment_entries() @@ -30,13 +30,13 @@ class BankTransaction(StatusUpdater): if allocated_amount: frappe.db.set_value(self.doctype, self.name, "allocated_amount", flt(allocated_amount)) - frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)) - flt(allocated_amount)) + frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)) - flt(allocated_amount)) else: frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0) - frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit))) + frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit))) - amount = self.debit or self.credit + amount = self.deposit or self.withdrawal if amount == self.allocated_amount: frappe.db.set_value(self.doctype, self.name, "status", "Reconciled") @@ -44,18 +44,11 @@ class BankTransaction(StatusUpdater): def clear_linked_payment_entries(self): for payment_entry in self.payment_entries: - allocated_amount = get_total_allocated_amount(payment_entry) - paid_amount = get_paid_amount(payment_entry, self.currency) + if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]: + self.clear_simple_entry(payment_entry) - if paid_amount and allocated_amount: - if flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount): - frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).").format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount))) - else: - if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]: - self.clear_simple_entry(payment_entry) - - elif payment_entry.payment_document == "Sales Invoice": - self.clear_sales_invoice(payment_entry) + elif payment_entry.payment_document == "Sales Invoice": + self.clear_sales_invoice(payment_entry) def clear_simple_entry(self, payment_entry): frappe.db.set_value(payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", self.date) @@ -112,3 +105,4 @@ def unclear_reference_payment(doctype, docname): frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None) return doc.payment_entry + diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py index e9fc5f0a1d..3b14e4efa0 100644 --- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py @@ -5,10 +5,11 @@ from __future__ import unicode_literals import frappe import unittest +import json 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.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry -from erpnext.accounts.page.bank_reconciliation.bank_reconciliation import reconcile, get_linked_payments +from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import reconcile_vouchers, get_linked_payments from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile test_dependencies = ["Item", "Cost Center"] @@ -17,7 +18,7 @@ class TestBankTransaction(unittest.TestCase): def setUp(self): make_pos_profile() add_transactions() - add_payments() + add_vouchers() def tearDown(self): for bt in frappe.get_all("Bank Transaction"): @@ -38,14 +39,18 @@ class TestBankTransaction(unittest.TestCase): # This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction. def test_linked_payments(self): bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic")) - linked_payments = get_linked_payments(bank_transaction.name) - self.assertTrue(linked_payments[0].party == "Conrad Electronic") + linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match']) + self.assertTrue(linked_payments[0][6] == "Conrad Electronic") # This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment def test_reconcile(self): bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G")) payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200)) - reconcile(bank_transaction.name, "Payment Entry", payment.name) + vouchers = json.dumps([{ + "payment_doctype":"Payment Entry", + "payment_name":payment.name, + "amount":bank_transaction.unallocated_amount}]) + reconcile_vouchers(bank_transaction.name, vouchers) unallocated_amount = frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount") self.assertTrue(unallocated_amount == 0) @@ -53,45 +58,40 @@ class TestBankTransaction(unittest.TestCase): clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date") self.assertTrue(clearance_date is not None) - # Check if ERPNext can correctly fetch a linked payment based on the party - def test_linked_payments_based_on_party(self): - bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G")) - linked_payments = get_linked_payments(bank_transaction.name) - self.assertTrue(len(linked_payments)==1) - # Check if ERPNext can correctly filter a linked payments based on the debit/credit amount def test_debit_credit_output(self): bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07")) - linked_payments = get_linked_payments(bank_transaction.name) - self.assertTrue(linked_payments[0].payment_type == "Pay") + linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match']) + print(linked_payments) + self.assertTrue(linked_payments[0][3]) # Check error if already reconciled def test_already_reconciled(self): bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G")) payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200)) - reconcile(bank_transaction.name, "Payment Entry", payment.name) + vouchers = json.dumps([{ + "payment_doctype":"Payment Entry", + "payment_name":payment.name, + "amount":bank_transaction.unallocated_amount}]) + reconcile_vouchers(bank_transaction.name, vouchers) bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G")) payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200)) - self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name) - - # Raise an error if creditor transaction vs creditor payment - def test_invalid_creditor_reconcilation(self): - bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio")) - payment = frappe.get_doc("Payment Entry", dict(party="Conrad Electronic", paid_amount=690)) - self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name) - - # Raise an error if debitor transaction vs debitor payment - def test_invalid_debitor_reconcilation(self): - bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07")) - payment = frappe.get_doc("Payment Entry", dict(party="Fayva", paid_amount=109080)) - self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name) + vouchers = json.dumps([{ + "payment_doctype":"Payment Entry", + "payment_name":payment.name, + "amount":bank_transaction.unallocated_amount}]) + self.assertRaises(frappe.ValidationError, reconcile_vouchers, bank_transaction_name=bank_transaction.name, vouchers=vouchers) # Raise an error if debitor transaction vs debitor payment def test_clear_sales_invoice(self): bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio")) payment = frappe.get_doc("Sales Invoice", dict(customer="Fayva", status=["=", "Paid"])) - reconcile(bank_transaction.name, "Sales Invoice", payment.name) + vouchers = json.dumps([{ + "payment_doctype":"Sales Invoice", + "payment_name":payment.name, + "amount":bank_transaction.unallocated_amount}]) + reconcile_vouchers(bank_transaction.name, vouchers=vouchers) self.assertEqual(frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0) self.assertTrue(frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date") is not None) @@ -126,7 +126,7 @@ def add_transactions(): "doctype": "Bank Transaction", "description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G", "date": "2018-10-23", - "debit": 1200, + "deposit": 1200, "currency": "INR", "bank_account": "Checking Account - Citi Bank" }).insert() @@ -136,7 +136,7 @@ def add_transactions(): "doctype": "Bank Transaction", "description":"1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G", "date": "2018-10-23", - "debit": 1700, + "deposit": 1700, "currency": "INR", "bank_account": "Checking Account - Citi Bank" }).insert() @@ -146,7 +146,7 @@ def add_transactions(): "doctype": "Bank Transaction", "description":"Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic", "date": "2018-10-26", - "debit": 690, + "withdrawal": 690, "currency": "INR", "bank_account": "Checking Account - Citi Bank" }).insert() @@ -156,7 +156,7 @@ def add_transactions(): "doctype": "Bank Transaction", "description":"Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07", "date": "2018-10-27", - "debit": 3900, + "deposit": 3900, "currency": "INR", "bank_account": "Checking Account - Citi Bank" }).insert() @@ -166,7 +166,7 @@ def add_transactions(): "doctype": "Bank Transaction", "description":"I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio", "date": "2018-10-27", - "credit": 109080, + "withdrawal": 109080, "currency": "INR", "bank_account": "Checking Account - Citi Bank" }).insert() @@ -174,7 +174,7 @@ def add_transactions(): frappe.flags.test_bank_transactions_created = True -def add_payments(): +def add_vouchers(): if frappe.flags.test_payments_created: return @@ -192,6 +192,7 @@ def add_payments(): pass pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690) + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") pe.reference_no = "Conrad Oct 18" pe.reference_date = "2018-10-24" @@ -242,10 +243,15 @@ def add_payments(): except frappe.DuplicateEntryError: pass - pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900) + pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save =1) + pi.cash_bank_account = "_Test Bank - _TC" + pi.insert() + pi.submit() pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") pe.reference_no = "Poore Simon's Oct 18" pe.reference_date = "2018-10-28" + pe.paid_amount = 690 + pe.received_amount = 690 pe.insert() pe.submit() @@ -295,4 +301,4 @@ def add_payments(): si.save() si.submit() - frappe.flags.test_payments_created = True \ No newline at end of file + frappe.flags.test_payments_created = True diff --git a/erpnext/accounts/page/bank_reconciliation/__init__.py b/erpnext/accounts/page/bank_reconciliation/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js deleted file mode 100644 index 6ae81d7402..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js +++ /dev/null @@ -1,583 +0,0 @@ -frappe.provide("erpnext.accounts"); - -frappe.pages['bank-reconciliation'].on_page_load = function(wrapper) { - new erpnext.accounts.bankReconciliation(wrapper); -} - -erpnext.accounts.bankReconciliation = class BankReconciliation { - constructor(wrapper) { - this.page = frappe.ui.make_app_page({ - parent: wrapper, - title: __("Bank Reconciliation"), - single_column: true - }); - this.parent = wrapper; - this.page = this.parent.page; - - this.check_plaid_status(); - this.make(); - } - - make() { - const me = this; - - me.$main_section = $(`
    `).appendTo(me.page.main); - const empty_state = __("Upload a bank statement, link or reconcile a bank account") - me.$main_section.append(`
    ${empty_state}
    `) - - me.page.add_field({ - fieldtype: 'Link', - label: __('Company'), - fieldname: 'company', - options: "Company", - onchange: function() { - if (this.value) { - me.company = this.value; - } else { - me.company = null; - me.bank_account = null; - } - } - }) - me.page.add_field({ - fieldtype: 'Link', - label: __('Bank Account'), - fieldname: 'bank_account', - options: "Bank Account", - get_query: function() { - if(!me.company) { - frappe.throw(__("Please select company first")); - return - } - - return { - filters: { - "company": me.company - } - } - }, - onchange: function() { - if (this.value) { - me.bank_account = this.value; - me.add_actions(); - } else { - me.bank_account = null; - me.page.hide_actions_menu(); - } - } - }) - } - - check_plaid_status() { - const me = this; - frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => { - if (r && r.enabled === "1") { - me.plaid_status = "active" - } else { - me.plaid_status = "inactive" - } - }) - } - - add_actions() { - const me = this; - - me.page.show_menu() - - me.page.add_menu_item(__("Upload a statement"), function() { - me.clear_page_content(); - new erpnext.accounts.bankTransactionUpload(me); - }, true) - - if (me.plaid_status==="active") { - me.page.add_menu_item(__("Synchronize this account"), function() { - me.clear_page_content(); - new erpnext.accounts.bankTransactionSync(me); - }, true) - } - - me.page.add_menu_item(__("Reconcile this account"), function() { - me.clear_page_content(); - me.make_reconciliation_tool(); - }, true) - } - - clear_page_content() { - const me = this; - $(me.page.body).find('.frappe-list').remove(); - me.$main_section.empty(); - } - - make_reconciliation_tool() { - const me = this; - frappe.model.with_doctype("Bank Transaction", () => { - erpnext.accounts.ReconciliationList = new erpnext.accounts.ReconciliationTool({ - parent: me.parent, - doctype: "Bank Transaction" - }); - }) - } -} - - -erpnext.accounts.bankTransactionUpload = class bankTransactionUpload { - constructor(parent) { - this.parent = parent; - this.data = []; - - const assets = [ - "/assets/frappe/css/frappe-datatable.css", - "/assets/frappe/js/lib/clusterize.min.js", - "/assets/frappe/js/lib/Sortable.min.js", - "/assets/frappe/js/lib/frappe-datatable.js" - ]; - - frappe.require(assets, () => { - this.make(); - }); - } - - make() { - const me = this; - new frappe.ui.FileUploader({ - method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement', - allow_multiple: 0, - on_success: function(attachment, r) { - if (!r.exc && r.message) { - me.data = r.message; - me.setup_transactions_dom(); - me.create_datatable(); - me.add_primary_action(); - } - } - }) - } - - setup_transactions_dom() { - const me = this; - me.parent.$main_section.append('
    '); - } - - create_datatable() { - try { - this.datatable = new DataTable('.transactions-table', { - columns: this.data.columns, - data: this.data.data - }) - } - catch(err) { - 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) - } - - } - - add_primary_action() { - const me = this; - me.parent.page.set_primary_action(__("Submit"), function() { - me.add_bank_entries() - }, null, __("Creating bank entries...")) - } - - add_bank_entries() { - const me = this; - frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.create_bank_entries', - {columns: this.datatable.datamanager.columns, data: this.datatable.datamanager.data, bank_account: me.parent.bank_account} - ).then((result) => { - let result_title = result.errors == 0 ? __("{0} bank transaction(s) created", [result.success]) : __("{0} bank transaction(s) created and {1} errors", [result.success, result.errors]) - let result_msg = ` -
    -
    ${result_title}
    -
    ` - me.parent.page.clear_primary_action(); - me.parent.$main_section.empty(); - me.parent.$main_section.append(result_msg); - if (result.errors == 0) { - frappe.show_alert({message:__("All bank transactions have been created"), indicator:'green'}); - } else { - frappe.show_alert({message:__("Please check the error log for details about the import errors"), indicator:'red'}); - } - }) - } -} - -erpnext.accounts.bankTransactionSync = class bankTransactionSync { - constructor(parent) { - this.parent = parent; - this.data = []; - - this.init_config() - } - - init_config() { - const me = this; - frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_plaid_configuration') - .then(result => { - me.plaid_env = result.plaid_env; - me.client_name = result.client_name; - me.link_token = result.link_token; - me.sync_transactions(); - }) - } - - sync_transactions() { - const me = this; - frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (r) => { - frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', { - bank: r.bank, - bank_account: me.parent.bank_account, - freeze: true - }) - .then((result) => { - let result_title = (result && result.length > 0) - ? __("{0} bank transaction(s) created", [result.length]) - : __("This bank account is already synchronized"); - - let result_msg = ` -
    -
    ${result_title}
    -
    ` - - this.parent.$main_section.append(result_msg) - frappe.show_alert({ message: __("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator: 'green' }); - }) - }) - } -} - - -erpnext.accounts.ReconciliationTool = class ReconciliationTool extends frappe.views.BaseList { - constructor(opts) { - super(opts); - this.show(); - } - - setup_defaults() { - super.setup_defaults(); - - this.page_title = __("Bank Reconciliation"); - this.doctype = 'Bank Transaction'; - this.fields = ['date', 'description', 'debit', 'credit', 'currency'] - - } - - setup_view() { - this.render_header(); - } - - setup_side_bar() { - // - } - - make_standard_filters() { - // - } - - freeze() { - this.$result.find('.list-count').html(`${__('Refreshing')}...`); - } - - get_args() { - const args = super.get_args(); - - return Object.assign({}, args, { - ...args.filters.push(["Bank Transaction", "docstatus", "=", 1], - ["Bank Transaction", "unallocated_amount", ">", 0]) - }); - - } - - update_data(r) { - let data = r.message || []; - - if (this.start === 0) { - this.data = data; - } else { - this.data = this.data.concat(data); - } - } - - render() { - const me = this; - this.$result.find('.list-row-container').remove(); - $('[data-fieldname="name"]').remove(); - me.data.map((value) => { - const row = $('
    ').data("data", value).appendTo(me.$result).get(0); - new erpnext.accounts.ReconciliationRow(row, value); - }) - } - - render_header() { - const me = this; - if ($(this.wrapper).find('.transaction-header').length === 0) { - me.$result.append(frappe.render_template("bank_transaction_header")); - } - } -} - -erpnext.accounts.ReconciliationRow = class ReconciliationRow { - constructor(row, data) { - this.data = data; - this.row = row; - this.make(); - this.bind_events(); - } - - make() { - $(this.row).append(frappe.render_template("bank_transaction_row", this.data)) - } - - bind_events() { - const me = this; - $(me.row).on('click', '.clickable-section', function() { - me.bank_entry = $(this).attr("data-name"); - me.show_dialog($(this).attr("data-name")); - }) - - $(me.row).on('click', '.new-reconciliation', function() { - me.bank_entry = $(this).attr("data-name"); - me.show_dialog($(this).attr("data-name")); - }) - - $(me.row).on('click', '.new-payment', function() { - me.bank_entry = $(this).attr("data-name"); - me.new_payment(); - }) - - $(me.row).on('click', '.new-invoice', function() { - me.bank_entry = $(this).attr("data-name"); - me.new_invoice(); - }) - - $(me.row).on('click', '.new-expense', function() { - me.bank_entry = $(this).attr("data-name"); - me.new_expense(); - }) - } - - new_payment() { - const me = this; - const paid_amount = me.data.credit > 0 ? me.data.credit : me.data.debit; - const payment_type = me.data.credit > 0 ? "Receive": "Pay"; - const party_type = me.data.credit > 0 ? "Customer": "Supplier"; - - frappe.new_doc("Payment Entry", {"payment_type": payment_type, "paid_amount": paid_amount, - "party_type": party_type, "paid_from": me.data.bank_account}) - } - - new_invoice() { - const me = this; - const invoice_type = me.data.credit > 0 ? "Sales Invoice" : "Purchase Invoice"; - - frappe.new_doc(invoice_type) - } - - new_expense() { - frappe.new_doc("Expense Claim") - } - - - show_dialog(data) { - const me = this; - - frappe.db.get_value("Bank Account", me.data.bank_account, "account", (r) => { - me.gl_account = r.account; - }) - - frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments', - { bank_transaction: data, freeze: true, freeze_message: __("Finding linked payments") } - ).then((result) => { - me.make_dialog(result) - }) - } - - make_dialog(data) { - const me = this; - me.selected_payment = null; - - const fields = [ - { - fieldtype: 'Section Break', - fieldname: 'section_break_1', - label: __('Automatic Reconciliation') - }, - { - fieldtype: 'HTML', - fieldname: 'payment_proposals' - }, - { - fieldtype: 'Section Break', - fieldname: 'section_break_2', - label: __('Search for a payment') - }, - { - fieldtype: 'Link', - fieldname: 'payment_doctype', - options: 'DocType', - label: 'Payment DocType', - get_query: () => { - return { - filters : { - "name": ["in", ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Expense Claim"]] - } - } - }, - }, - { - fieldtype: 'Column Break', - fieldname: 'column_break_1', - }, - { - fieldtype: 'Dynamic Link', - fieldname: 'payment_entry', - options: 'payment_doctype', - label: 'Payment Document', - get_query: () => { - let dt = this.dialog.fields_dict.payment_doctype.value; - if (dt === "Payment Entry") { - return { - query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.payment_entry_query", - filters : { - "bank_account": this.data.bank_account, - "company": this.data.company - } - } - } else if (dt === "Journal Entry") { - return { - query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.journal_entry_query", - filters : { - "bank_account": this.data.bank_account, - "company": this.data.company - } - } - } else if (dt === "Sales Invoice") { - return { - query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.sales_invoices_query" - } - } else if (dt === "Purchase Invoice") { - return { - filters : [ - ["Purchase Invoice", "ifnull(clearance_date, '')", "=", ""], - ["Purchase Invoice", "docstatus", "=", 1], - ["Purchase Invoice", "company", "=", this.data.company] - ] - } - } else if (dt === "Expense Claim") { - return { - filters : [ - ["Expense Claim", "ifnull(clearance_date, '')", "=", ""], - ["Expense Claim", "docstatus", "=", 1], - ["Expense Claim", "company", "=", this.data.company] - ] - } - } - }, - onchange: function() { - if (me.selected_payment !== this.value) { - me.selected_payment = this.value; - me.display_payment_details(this); - } - } - }, - { - fieldtype: 'Section Break', - fieldname: 'section_break_3' - }, - { - fieldtype: 'HTML', - fieldname: 'payment_details' - }, - ]; - - me.dialog = new frappe.ui.Dialog({ - title: __("Choose a corresponding payment"), - fields: fields, - size: "large" - }); - - const proposals_wrapper = me.dialog.fields_dict.payment_proposals.$wrapper; - if (data && data.length > 0) { - proposals_wrapper.append(frappe.render_template("linked_payment_header")); - data.map(value => { - proposals_wrapper.append(frappe.render_template("linked_payment_row", value)) - }) - } else { - const empty_data_msg = __("ERPNext could not find any matching payment entry") - proposals_wrapper.append(`
    ${empty_data_msg}
    `) - } - - $(me.dialog.body).on('click', '.reconciliation-btn', (e) => { - const payment_entry = $(e.target).attr('data-name'); - const payment_doctype = $(e.target).attr('data-doctype'); - frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.reconcile', - {bank_transaction: me.bank_entry, payment_doctype: payment_doctype, payment_name: payment_entry}) - .then((result) => { - setTimeout(function(){ - erpnext.accounts.ReconciliationList.refresh(); - }, 2000); - me.dialog.hide(); - }) - }) - - me.dialog.show(); - } - - display_payment_details(event) { - const me = this; - if (event.value) { - let dt = me.dialog.fields_dict.payment_doctype.value; - me.dialog.fields_dict['payment_details'].$wrapper.empty(); - frappe.db.get_doc(dt, event.value) - .then(doc => { - let displayed_docs = [] - let payment = [] - if (dt === "Payment Entry") { - payment.currency = doc.payment_type == "Receive" ? doc.paid_to_account_currency : doc.paid_from_account_currency; - payment.doctype = dt - payment.posting_date = doc.posting_date; - payment.party = doc.party; - payment.reference_no = doc.reference_no; - payment.reference_date = doc.reference_date; - payment.paid_amount = doc.paid_amount; - payment.name = doc.name; - displayed_docs.push(payment); - } else if (dt === "Journal Entry") { - doc.accounts.forEach(payment => { - if (payment.account === me.gl_account) { - payment.doctype = dt; - payment.posting_date = doc.posting_date; - payment.party = doc.pay_to_recd_from; - payment.reference_no = doc.cheque_no; - payment.reference_date = doc.cheque_date; - payment.currency = payment.account_currency; - payment.paid_amount = payment.credit > 0 ? payment.credit : payment.debit; - payment.name = doc.name; - displayed_docs.push(payment); - } - }) - } else if (dt === "Sales Invoice") { - doc.payments.forEach(payment => { - if (payment.clearance_date === null || payment.clearance_date === "") { - payment.doctype = dt; - payment.posting_date = doc.posting_date; - payment.party = doc.customer; - payment.reference_no = doc.remarks; - payment.currency = doc.currency; - payment.paid_amount = payment.amount; - payment.name = doc.name; - displayed_docs.push(payment); - } - }) - } - - const details_wrapper = me.dialog.fields_dict.payment_details.$wrapper; - details_wrapper.append(frappe.render_template("linked_payment_header")); - displayed_docs.forEach(payment => { - details_wrapper.append(frappe.render_template("linked_payment_row", payment)); - }) - }) - } - - } -} diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json deleted file mode 100644 index feea36860b..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "content": null, - "creation": "2018-11-24 12:03:14.646669", - "docstatus": 0, - "doctype": "Page", - "idx": 0, - "modified": "2018-11-24 12:03:14.646669", - "modified_by": "Administrator", - "module": "Accounts", - "name": "bank-reconciliation", - "owner": "Administrator", - "page_name": "bank-reconciliation", - "roles": [ - { - "role": "System Manager" - }, - { - "role": "Accounts Manager" - }, - { - "role": "Accounts User" - } - ], - "script": null, - "standard": "Yes", - "style": null, - "system_page": 0, - "title": "Bank Reconciliation" -} \ No newline at end of file diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py deleted file mode 100644 index 8abe20c00a..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py +++ /dev/null @@ -1,369 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -import difflib -from frappe.utils import flt -from six import iteritems -from erpnext import get_company_currency - -@frappe.whitelist() -def reconcile(bank_transaction, payment_doctype, payment_name): - transaction = frappe.get_doc("Bank Transaction", bank_transaction) - payment_entry = frappe.get_doc(payment_doctype, payment_name) - - account = frappe.db.get_value("Bank Account", transaction.bank_account, "account") - gl_entry = frappe.get_doc("GL Entry", dict(account=account, voucher_type=payment_doctype, voucher_no=payment_name)) - - if payment_doctype == "Payment Entry" and payment_entry.unallocated_amount > transaction.unallocated_amount: - frappe.throw(_("The unallocated amount of Payment Entry {0} is greater than the Bank Transaction's unallocated amount").format(payment_name)) - - if transaction.unallocated_amount == 0: - frappe.throw(_("This bank transaction is already fully reconciled")) - - if transaction.credit > 0 and gl_entry.credit > 0: - frappe.throw(_("The selected payment entry should be linked with a debtor bank transaction")) - - if transaction.debit > 0 and gl_entry.debit > 0: - frappe.throw(_("The selected payment entry should be linked with a creditor bank transaction")) - - add_payment_to_transaction(transaction, payment_entry, gl_entry) - - return 'reconciled' - -def add_payment_to_transaction(transaction, payment_entry, gl_entry): - gl_amount, transaction_amount = (gl_entry.credit, transaction.debit) if gl_entry.credit > 0 else (gl_entry.debit, transaction.credit) - allocated_amount = gl_amount if gl_amount <= transaction_amount else transaction_amount - transaction.append("payment_entries", { - "payment_document": payment_entry.doctype, - "payment_entry": payment_entry.name, - "allocated_amount": allocated_amount - }) - - transaction.save() - transaction.update_allocations() - -@frappe.whitelist() -def get_linked_payments(bank_transaction): - transaction = frappe.get_doc("Bank Transaction", bank_transaction) - bank_account = frappe.db.get_values("Bank Account", transaction.bank_account, ["account", "company"], as_dict=True) - - # Get all payment entries with a matching amount - amount_matching = check_matching_amount(bank_account[0].account, bank_account[0].company, transaction) - - # Get some data from payment entries linked to a corresponding bank transaction - description_matching = get_matching_descriptions_data(bank_account[0].company, transaction) - - if amount_matching: - return check_amount_vs_description(amount_matching, description_matching) - - elif description_matching: - description_matching = filter(lambda x: not x.get('clearance_date'), description_matching) - if not description_matching: - return [] - - return sorted(list(description_matching), key = lambda x: x["posting_date"], reverse=True) - - else: - return [] - -def check_matching_amount(bank_account, company, transaction): - payments = [] - amount = transaction.credit if transaction.credit > 0 else transaction.debit - - payment_type = "Receive" if transaction.credit > 0 else "Pay" - account_from_to = "paid_to" if transaction.credit > 0 else "paid_from" - currency_field = "paid_to_account_currency as currency" if transaction.credit > 0 else "paid_from_account_currency as currency" - - payment_entries = frappe.get_all("Payment Entry", fields=["'Payment Entry' as doctype", "name", "paid_amount", "payment_type", "reference_no", "reference_date", - "party", "party_type", "posting_date", "{0}".format(currency_field)], filters=[["paid_amount", "like", "{0}%".format(amount)], - ["docstatus", "=", "1"], ["payment_type", "=", [payment_type, "Internal Transfer"]], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]]) - - jea_side = "debit" if transaction.credit > 0 else "credit" - journal_entries = frappe.db.sql(f""" - SELECT - 'Journal Entry' as doctype, je.name, je.posting_date, je.cheque_no as reference_no, - jea.account_currency as currency, je.pay_to_recd_from as party, je.cheque_date as reference_date, - jea.{jea_side}_in_account_currency as paid_amount - FROM - `tabJournal Entry Account` as jea - JOIN - `tabJournal Entry` as je - ON - jea.parent = je.name - WHERE - (je.clearance_date is null or je.clearance_date='0000-00-00') - AND - jea.account = %(bank_account)s - AND - jea.{jea_side}_in_account_currency like %(txt)s - AND - je.docstatus = 1 - """, { - 'bank_account': bank_account, - 'txt': '%%%s%%' % amount - }, as_dict=True) - - if transaction.credit > 0: - sales_invoices = frappe.db.sql(""" - SELECT - 'Sales Invoice' as doctype, si.name, si.customer as party, - si.posting_date, sip.amount as paid_amount - FROM - `tabSales Invoice Payment` as sip - JOIN - `tabSales Invoice` as si - ON - sip.parent = si.name - WHERE - (sip.clearance_date is null or sip.clearance_date='0000-00-00') - AND - sip.account = %s - AND - sip.amount like %s - AND - si.docstatus = 1 - """, (bank_account, amount), as_dict=True) - else: - sales_invoices = [] - - if transaction.debit > 0: - purchase_invoices = frappe.get_all("Purchase Invoice", - fields = ["'Purchase Invoice' as doctype", "name", "paid_amount", "supplier as party", "posting_date", "currency"], - filters=[ - ["paid_amount", "like", "{0}%".format(amount)], - ["docstatus", "=", "1"], - ["is_paid", "=", "1"], - ["ifnull(clearance_date, '')", "=", ""], - ["cash_bank_account", "=", "{0}".format(bank_account)] - ] - ) - - mode_of_payments = [x["parent"] for x in frappe.db.get_list("Mode of Payment Account", - filters={"default_account": bank_account}, fields=["parent"])] - - company_currency = get_company_currency(company) - - expense_claims = frappe.get_all("Expense Claim", - fields=["'Expense Claim' as doctype", "name", "total_sanctioned_amount as paid_amount", - "employee as party", "posting_date", "'{0}' as currency".format(company_currency)], - filters=[ - ["total_sanctioned_amount", "like", "{0}%".format(amount)], - ["docstatus", "=", "1"], - ["is_paid", "=", "1"], - ["ifnull(clearance_date, '')", "=", ""], - ["mode_of_payment", "in", "{0}".format(tuple(mode_of_payments))] - ] - ) - else: - purchase_invoices = expense_claims = [] - - for data in [payment_entries, journal_entries, sales_invoices, purchase_invoices, expense_claims]: - if data: - payments.extend(data) - - return payments - -def get_matching_descriptions_data(company, transaction): - if not transaction.description : - return [] - - bank_transactions = frappe.db.sql(""" - SELECT - bt.name, bt.description, bt.date, btp.payment_document, btp.payment_entry - FROM - `tabBank Transaction` as bt - LEFT JOIN - `tabBank Transaction Payments` as btp - ON - bt.name = btp.parent - WHERE - bt.allocated_amount > 0 - AND - bt.docstatus = 1 - """, as_dict=True) - - selection = [] - for bank_transaction in bank_transactions: - if bank_transaction.description: - seq=difflib.SequenceMatcher(lambda x: x == " ", transaction.description, bank_transaction.description) - - if seq.ratio() > 0.6: - bank_transaction["ratio"] = seq.ratio() - selection.append(bank_transaction) - - document_types = set([x["payment_document"] for x in selection]) - - links = {} - for document_type in document_types: - links[document_type] = [x["payment_entry"] for x in selection if x["payment_document"]==document_type] - - - data = [] - company_currency = get_company_currency(company) - for key, value in iteritems(links): - if key == "Payment Entry": - data.extend(frappe.get_all("Payment Entry", filters=[["name", "in", value]], - fields=["'Payment Entry' as doctype", "posting_date", "party", "reference_no", - "reference_date", "paid_amount", "paid_to_account_currency as currency", "clearance_date"])) - if key == "Journal Entry": - journal_entries = frappe.get_all("Journal Entry", filters=[["name", "in", value]], - fields=["name", "'Journal Entry' as doctype", "posting_date", - "pay_to_recd_from as party", "cheque_no as reference_no", "cheque_date as reference_date", - "total_credit as paid_amount", "clearance_date"]) - for journal_entry in journal_entries: - journal_entry_accounts = frappe.get_all("Journal Entry Account", filters={"parenttype": journal_entry["doctype"], "parent": journal_entry["name"]}, fields=["account_currency"]) - journal_entry["currency"] = journal_entry_accounts[0]["account_currency"] if journal_entry_accounts else company_currency - data.extend(journal_entries) - if key == "Sales Invoice": - data.extend(frappe.get_all("Sales Invoice", filters=[["name", "in", value]], fields=["'Sales Invoice' as doctype", "posting_date", "customer_name as party", "paid_amount", "currency"])) - if key == "Purchase Invoice": - data.extend(frappe.get_all("Purchase Invoice", filters=[["name", "in", value]], fields=["'Purchase Invoice' as doctype", "posting_date", "supplier_name as party", "paid_amount", "currency"])) - if key == "Expense Claim": - expense_claims = frappe.get_all("Expense Claim", filters=[["name", "in", value]], fields=["'Expense Claim' as doctype", "posting_date", "employee_name as party", "total_amount_reimbursed as paid_amount"]) - data.extend([dict(x,**{"currency": company_currency}) for x in expense_claims]) - - return data - -def check_amount_vs_description(amount_matching, description_matching): - result = [] - - if description_matching: - for am_match in amount_matching: - for des_match in description_matching: - if des_match.get("clearance_date"): - continue - - if am_match["party"] == des_match["party"]: - if am_match not in result: - result.append(am_match) - continue - - if "reference_no" in am_match and "reference_no" in des_match: - # Sequence Matcher does not handle None as input - am_reference = am_match["reference_no"] or "" - des_reference = des_match["reference_no"] or "" - - if difflib.SequenceMatcher(lambda x: x == " ", am_reference, des_reference).ratio() > 70: - if am_match not in result: - result.append(am_match) - if result: - return sorted(result, key = lambda x: x["posting_date"], reverse=True) - else: - return sorted(amount_matching, key = lambda x: x["posting_date"], reverse=True) - - else: - return sorted(amount_matching, key = lambda x: x["posting_date"], reverse=True) - -def get_matching_transactions_payments(description_matching): - payments = [x["payment_entry"] for x in description_matching] - - payment_by_ratio = {x["payment_entry"]: x["ratio"] for x in description_matching} - - if payments: - reference_payment_list = frappe.get_all("Payment Entry", fields=["name", "paid_amount", "payment_type", "reference_no", "reference_date", - "party", "party_type", "posting_date", "paid_to_account_currency"], filters=[["name", "in", payments]]) - - return sorted(reference_payment_list, key=lambda x: payment_by_ratio[x["name"]]) - - else: - return [] - -@frappe.whitelist() -@frappe.validate_and_sanitize_search_inputs -def payment_entry_query(doctype, txt, searchfield, start, page_len, filters): - account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account") - if not account: - return - - return frappe.db.sql(""" - SELECT - name, party, paid_amount, received_amount, reference_no - FROM - `tabPayment Entry` - WHERE - (clearance_date is null or clearance_date='0000-00-00') - AND (paid_from = %(account)s or paid_to = %(account)s) - AND (name like %(txt)s or party like %(txt)s) - AND docstatus = 1 - ORDER BY - if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), name - LIMIT - %(start)s, %(page_len)s""", - { - 'txt': "%%%s%%" % txt, - '_txt': txt.replace("%", ""), - 'start': start, - 'page_len': page_len, - 'account': account - } - ) - -@frappe.whitelist() -@frappe.validate_and_sanitize_search_inputs -def journal_entry_query(doctype, txt, searchfield, start, page_len, filters): - account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account") - - return frappe.db.sql(""" - SELECT - jea.parent, je.pay_to_recd_from, - if(jea.debit_in_account_currency > 0, jea.debit_in_account_currency, jea.credit_in_account_currency) - FROM - `tabJournal Entry Account` as jea - LEFT JOIN - `tabJournal Entry` as je - ON - jea.parent = je.name - WHERE - (je.clearance_date is null or je.clearance_date='0000-00-00') - AND - jea.account = %(account)s - AND - (jea.parent like %(txt)s or je.pay_to_recd_from like %(txt)s) - AND - je.docstatus = 1 - ORDER BY - if(locate(%(_txt)s, jea.parent), locate(%(_txt)s, jea.parent), 99999), - jea.parent - LIMIT - %(start)s, %(page_len)s""", - { - 'txt': "%%%s%%" % txt, - '_txt': txt.replace("%", ""), - 'start': start, - 'page_len': page_len, - 'account': account - } - ) - -@frappe.whitelist() -@frappe.validate_and_sanitize_search_inputs -def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters): - return frappe.db.sql(""" - SELECT - sip.parent, si.customer, sip.amount, sip.mode_of_payment - FROM - `tabSales Invoice Payment` as sip - LEFT JOIN - `tabSales Invoice` as si - ON - sip.parent = si.name - WHERE - (sip.clearance_date is null or sip.clearance_date='0000-00-00') - AND - (sip.parent like %(txt)s or si.customer like %(txt)s) - ORDER BY - if(locate(%(_txt)s, sip.parent), locate(%(_txt)s, sip.parent), 99999), - sip.parent - LIMIT - %(start)s, %(page_len)s""", - { - 'txt': "%%%s%%" % txt, - '_txt': txt.replace("%", ""), - 'start': start, - 'page_len': page_len - } - ) diff --git a/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html b/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html deleted file mode 100644 index 94f183b793..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html +++ /dev/null @@ -1,21 +0,0 @@ -
    -
    - -
    - {{ __("Description") }} -
    - - - -
    -
    -
    -
    diff --git a/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html b/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html deleted file mode 100644 index 742b84c63f..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html +++ /dev/null @@ -1,36 +0,0 @@ -
    diff --git a/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html b/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html deleted file mode 100644 index 4542c36e0d..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html +++ /dev/null @@ -1,21 +0,0 @@ -
    -
    -
    - {{ __("Payment Name") }} -
    -
    - {{ __("Reference Date") }} -
    - - -
    - {{ __("Reference Number") }} -
    -
    -
    -
    -
    diff --git a/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html b/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html deleted file mode 100644 index bdbc9fce03..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html +++ /dev/null @@ -1,36 +0,0 @@ -
    -
    -
    - {{ name }} -
    -
    - {% if (typeof reference_date !== "undefined") %} - {%= frappe.datetime.str_to_user(reference_date) %} - {% else %} - {% if (typeof posting_date !== "undefined") %} - {%= frappe.datetime.str_to_user(posting_date) %} - {% endif %} - {% endif %} -
    - - -
    - {% if (typeof reference_no !== "undefined") %} - {{ reference_no }} - {% else %} - {{ "" }} - {% endif %} -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index 70c7f3fe5d..21f6fee79c 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -204,8 +204,8 @@ def new_bank_transaction(transaction): "date": getdate(transaction["date"]), "status": status, "bank_account": bank_account, - "debit": debit, - "credit": credit, + "deposit": debit, + "withdrawal": credit, "currency": transaction["iso_currency_code"], "transaction_id": transaction["transaction_id"], "reference_number": transaction["payment_meta"]["reference_number"], diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 01d9ad32e7..ba31feeefc 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -754,4 +754,5 @@ erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021 erpnext.patches.v12_0.add_state_code_for_ladakh erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl -erpnext.patches.v13_0.update_vehicle_no_reqd_condition \ No newline at end of file +erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes +erpnext.patches.v13_0.update_vehicle_no_reqd_condition diff --git a/erpnext/patches/v11_1/update_bank_transaction_status.py b/erpnext/patches/v11_1/update_bank_transaction_status.py index 1acdfcccf9..544bc5e691 100644 --- a/erpnext/patches/v11_1/update_bank_transaction_status.py +++ b/erpnext/patches/v11_1/update_bank_transaction_status.py @@ -7,9 +7,20 @@ import frappe def execute(): frappe.reload_doc("accounts", "doctype", "bank_transaction") - frappe.db.sql(""" UPDATE `tabBank Transaction` - SET status = 'Reconciled' - WHERE - status = 'Settled' and (debit = allocated_amount or credit = allocated_amount) - and ifnull(allocated_amount, 0) > 0 - """) \ No newline at end of file + bank_transaction_fields = frappe.get_meta("Bank Transaction").get_valid_columns() + + if 'debit' in bank_transaction_fields: + frappe.db.sql(""" UPDATE `tabBank Transaction` + SET status = 'Reconciled' + WHERE + status = 'Settled' and (debit = allocated_amount or credit = allocated_amount) + and ifnull(allocated_amount, 0) > 0 + """) + + elif 'deposit' in bank_transaction_fields: + frappe.db.sql(""" UPDATE `tabBank Transaction` + SET status = 'Reconciled' + WHERE + status = 'Settled' and (deposit = allocated_amount or withdrawal = allocated_amount) + and ifnull(allocated_amount, 0) > 0 + """) \ No newline at end of file diff --git a/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py b/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py new file mode 100644 index 0000000000..af1f6e7ec1 --- /dev/null +++ b/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py @@ -0,0 +1,26 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe +from frappe.model.utils.rename_field import rename_field + +def execute(): + doctypes = [ + "Bank Statement Settings", + "Bank Statement Settings Item", + "Bank Statement Transaction Entry", + "Bank Statement Transaction Invoice Item", + "Bank Statement Transaction Payment Item", + "Bank Statement Transaction Settings Item", + "Bank Statement Transaction Settings", + ] + + for doctype in doctypes: + frappe.delete_doc("DocType", doctype, force=1) + + frappe.delete_doc("Page", "bank-reconciliation", force=1) + + rename_field("Bank Transaction", "debit", "deposit") + rename_field("Bank Transaction", "credit", "withdrawal") diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 7326238273..7a3cb838a9 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -61,5 +61,10 @@ "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" + ], + "js/bank-reconciliation-tool.min.js": [ + "public/js/bank_reconciliation_tool/data_table_manager.js", + "public/js/bank_reconciliation_tool/number_card.js", + "public/js/bank_reconciliation_tool/dialog_manager.js" ] } diff --git a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js new file mode 100644 index 0000000000..5bb58faf2f --- /dev/null +++ b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js @@ -0,0 +1,220 @@ +frappe.provide("erpnext.accounts.bank_reconciliation"); + +erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager { + constructor(opts) { + Object.assign(this, opts); + this.dialog_manager = new erpnext.accounts.bank_reconciliation.DialogManager( + this.company, + this.bank_account + ); + this.make_dt(); + } + + make_dt() { + var me = this; + frappe.call({ + method: + "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions", + args: { + bank_account: this.bank_account, + }, + callback: function (response) { + me.format_data(response.message); + me.get_dt_columns(); + me.get_datatable(); + me.set_listeners(); + }, + }); + } + + get_dt_columns() { + this.columns = [ + { + name: "Date", + editable: false, + width: 100, + }, + + { + name: "Party Type", + editable: false, + width: 95, + }, + { + name: "Party", + editable: false, + width: 100, + }, + { + name: "Description", + editable: false, + width: 350, + }, + { + name: "Deposit", + editable: false, + width: 100, + format: (value) => + "" + + format_currency(value, this.currency) + + "", + }, + { + name: "Withdrawal", + editable: false, + width: 100, + format: (value) => + "" + + format_currency(value, this.currency) + + "", + }, + { + name: "Unallocated Amount", + editable: false, + width: 100, + format: (value) => + "" + + format_currency(value, this.currency) + + "", + }, + { + name: "Reference Number", + editable: false, + width: 140, + }, + { + name: "Actions", + editable: false, + sortable: false, + focusable: false, + dropdown: false, + width: 80, + }, + ]; + } + + format_data(transactions) { + this.transactions = []; + if (transactions[0]) { + this.currency = transactions[0]["currency"]; + } + this.transaction_dt_map = {}; + let length; + transactions.forEach((row) => { + length = this.transactions.push(this.format_row(row)); + this.transaction_dt_map[row["name"]] = length - 1; + }); + } + + format_row(row) { + return [ + row["date"], + row["party_type"], + row["party"], + row["description"], + row["deposit"], + row["withdrawal"], + row["unallocated_amount"], + row["reference_number"], + ` + +
    +
    +
    ${log.exception}
    +
    +
    `; + } + let indicator_color = log.success ? "green" : "red"; + let title = log.success ? __("Success") : __("Failure"); + + if (frm.doc.show_failed_logs && log.success) { + return ""; + } + + return ` + ${log.row_indexes.join(", ")} + +
    ${title}
    + + + ${html} + + `; + }) + .join(""); + + if (!rows && frm.doc.show_failed_logs) { + rows = ` + ${__("No failed logs")} + `; + } + + frm.get_field("import_log_preview").$wrapper.html(` + + + + + + + ${rows} +
    ${__("Row Number")}${__("Status")}${__("Message")}
    + `); + }, + + show_missing_link_values(frm, missing_link_values) { + let can_be_created_automatically = missing_link_values.every( + (d) => d.has_one_mandatory_field + ); + + let html = missing_link_values + .map((d) => { + let doctype = d.doctype; + let values = d.missing_values; + return ` +
    ${doctype}
    +
      ${values.map((v) => `
    • ${v}
    • `).join("")}
    + `; + }) + .join(""); + + if (can_be_created_automatically) { + // prettier-ignore + let message = __('There are some linked records which needs to be created before we can import your file. Do you want to create the following missing records automatically?'); + frappe.confirm(message + html, () => { + frm.call("create_missing_link_values", { + missing_link_values, + }).then((r) => { + let records = r.message; + frappe.msgprint(__( + "Created {0} records successfully.", [ + records.length, + ] + )); + }); + }); + } else { + frappe.msgprint( + // prettier-ignore + __('The following records needs to be created before we can import your file.') + html + ); + } + }, +}); diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json new file mode 100644 index 0000000000..5e913cc2aa --- /dev/null +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json @@ -0,0 +1,227 @@ +{ + "actions": [], + "autoname": "format:Bank Statement Import on {creation}", + "beta": 1, + "creation": "2019-08-04 14:16:08.318714", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "bank_account", + "bank", + "column_break_4", + "google_sheets_url", + "refresh_google_sheet", + "html_5", + "import_file", + "download_template", + "status", + "template_options", + "import_warnings_section", + "template_warnings", + "import_warnings", + "section_import_preview", + "import_preview", + "import_log_section", + "import_log", + "show_failed_logs", + "import_log_preview", + "reference_doctype", + "import_type", + "submit_after_import", + "mute_emails" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "bank_account", + "fieldtype": "Link", + "label": "Bank Account", + "options": "Bank Account", + "reqd": 1, + "set_only_once": 1 + }, + { + "depends_on": "eval:doc.bank_account", + "fetch_from": "bank_account.bank", + "fieldname": "bank", + "fieldtype": "Link", + "label": "Bank", + "options": "Bank", + "read_only": 1, + "set_only_once": 1 + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "download_template", + "fieldtype": "Button", + "label": "Download Template" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "import_file", + "fieldtype": "Attach", + "in_list_view": 1, + "label": "Import File" + }, + { + "fieldname": "import_preview", + "fieldtype": "HTML", + "label": "Import Preview" + }, + { + "fieldname": "section_import_preview", + "fieldtype": "Section Break", + "label": "Preview" + }, + { + "fieldname": "template_options", + "fieldtype": "Code", + "hidden": 1, + "label": "Template Options", + "options": "JSON", + "read_only": 1 + }, + { + "fieldname": "import_log", + "fieldtype": "Code", + "label": "Import Log", + "options": "JSON" + }, + { + "fieldname": "import_log_section", + "fieldtype": "Section Break", + "label": "Import Log" + }, + { + "fieldname": "import_log_preview", + "fieldtype": "HTML", + "label": "Import Log Preview" + }, + { + "default": "Pending", + "fieldname": "status", + "fieldtype": "Select", + "hidden": 1, + "label": "Status", + "options": "Pending\nSuccess\nPartial Success\nError", + "read_only": 1 + }, + { + "fieldname": "template_warnings", + "fieldtype": "Code", + "hidden": 1, + "label": "Template Warnings", + "options": "JSON" + }, + { + "fieldname": "import_warnings_section", + "fieldtype": "Section Break", + "label": "Import File Errors and Warnings" + }, + { + "fieldname": "import_warnings", + "fieldtype": "HTML", + "label": "Import Warnings" + }, + { + "default": "0", + "fieldname": "show_failed_logs", + "fieldtype": "Check", + "label": "Show Failed Logs" + }, + { + "depends_on": "eval:!doc.__islocal && !doc.import_file", + "fieldname": "html_5", + "fieldtype": "HTML", + "options": "
    Or
    " + }, + { + "depends_on": "eval:!doc.__islocal && !doc.import_file\n", + "description": "Must be a publicly accessible Google Sheets URL", + "fieldname": "google_sheets_url", + "fieldtype": "Data", + "label": "Import from Google Sheets" + }, + { + "depends_on": "eval:doc.google_sheets_url && !doc.__unsaved", + "fieldname": "refresh_google_sheet", + "fieldtype": "Button", + "label": "Refresh Google Sheet" + }, + { + "default": "Bank Transaction", + "fieldname": "reference_doctype", + "fieldtype": "Link", + "hidden": 1, + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "reqd": 1, + "set_only_once": 1 + }, + { + "default": "Insert New Records", + "fieldname": "import_type", + "fieldtype": "Select", + "hidden": 1, + "in_list_view": 1, + "label": "Import Type", + "options": "\nInsert New Records\nUpdate Existing Records", + "reqd": 1, + "set_only_once": 1 + }, + { + "default": "1", + "fieldname": "submit_after_import", + "fieldtype": "Check", + "hidden": 1, + "label": "Submit After Import", + "set_only_once": 1 + }, + { + "default": "1", + "fieldname": "mute_emails", + "fieldtype": "Check", + "hidden": 1, + "label": "Don't Send Emails", + "set_only_once": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + } + ], + "hide_toolbar": 1, + "links": [], + "modified": "2021-02-10 19:29:59.027325", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Bank Statement Import", + "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/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py new file mode 100644 index 0000000000..9f41b13f4b --- /dev/null +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import csv +import json +import re + +import openpyxl +from openpyxl.styles import Font +from openpyxl.utils import get_column_letter +from six import string_types + +import frappe +from frappe.core.doctype.data_import.importer import Importer, ImportFile +from frappe.utils.background_jobs import enqueue +from frappe.utils.xlsxutils import handle_html, ILLEGAL_CHARACTERS_RE +from frappe import _ + +from frappe.core.doctype.data_import.data_import import DataImport + +class BankStatementImport(DataImport): + def __init__(self, *args, **kwargs): + super(BankStatementImport, self).__init__(*args, **kwargs) + + def validate(self): + doc_before_save = self.get_doc_before_save() + if ( + not (self.import_file or self.google_sheets_url) + or (doc_before_save and doc_before_save.import_file != self.import_file) + or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url) + ): + + template_options_dict = {} + column_to_field_map = {} + bank = frappe.get_doc("Bank", self.bank) + for i in bank.bank_transaction_mapping: + column_to_field_map[i.file_field] = i.bank_transaction_field + template_options_dict["column_to_field_map"] = column_to_field_map + self.template_options = json.dumps(template_options_dict) + + self.template_warnings = "" + + self.validate_import_file() + self.validate_google_sheets_url() + + def start_import(self): + + from frappe.core.page.background_jobs.background_jobs import get_info + from frappe.utils.scheduler import is_scheduler_inactive + + if is_scheduler_inactive() and not frappe.flags.in_test: + frappe.throw( + _("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive") + ) + + enqueued_jobs = [d.get("job_name") for d in get_info()] + + if self.name not in enqueued_jobs: + enqueue( + start_import, + queue="default", + timeout=6000, + event="data_import", + job_name=self.name, + data_import=self.name, + bank_account=self.bank_account, + import_file_path=self.import_file, + bank=self.bank, + template_options=self.template_options, + now=frappe.conf.developer_mode or frappe.flags.in_test, + ) + return True + + return False + +@frappe.whitelist() +def get_preview_from_template(data_import, import_file=None, google_sheets_url=None): + return frappe.get_doc("Bank Statement Import", data_import).get_preview_from_template( + import_file, google_sheets_url + ) + +@frappe.whitelist() +def form_start_import(data_import): + return frappe.get_doc("Bank Statement Import", data_import).start_import() + +@frappe.whitelist() +def download_errored_template(data_import_name): + data_import = frappe.get_doc("Bank Statement Import", data_import_name) + data_import.export_errored_rows() + +def start_import(data_import, bank_account, import_file_path, bank, template_options): + """This method runs in background job""" + + update_mapping_db(bank, template_options) + + data_import = frappe.get_doc("Bank Statement Import", data_import) + + import_file = ImportFile("Bank Transaction", file = import_file_path, import_type="Insert New Records") + data = import_file.raw_data + + add_bank_account(data, bank_account) + write_files(import_file, data) + + try: + i = Importer(data_import.reference_doctype, data_import=data_import) + i.import_data() + except Exception: + frappe.db.rollback() + data_import.db_set("status", "Error") + frappe.log_error(title=data_import.name) + finally: + frappe.flags.in_import = False + + frappe.publish_realtime("data_import_refresh", {"data_import": data_import.name}) + +def update_mapping_db(bank, template_options): + bank = frappe.get_doc("Bank", bank) + for d in bank.bank_transaction_mapping: + d.delete() + + for d in json.loads(template_options)["column_to_field_map"].items(): + bank.append("bank_transaction_mapping", {"bank_transaction_field": d[1] ,"file_field": d[0]} ) + + bank.save() + +def add_bank_account(data, bank_account): + bank_account_loc = None + if "Bank Account" not in data[0]: + data[0].append("Bank Account") + else: + for loc, header in enumerate(data[0]): + if header == "Bank Account": + bank_account_loc = loc + + for row in data[1:]: + if bank_account_loc: + row[bank_account_loc] = bank_account + else: + row.append(bank_account) + +def write_files(import_file, data): + full_file_path = import_file.file_doc.get_full_path() + parts = import_file.file_doc.get_extension() + extension = parts[1] + extension = extension.lstrip(".") + + if extension == "csv": + with open(full_file_path, 'w', newline='') as file: + writer = csv.writer(file) + writer.writerows(data) + elif extension == "xlsx" or "xls": + write_xlsx(data, "trans", file_path = full_file_path) + +def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None): + # from xlsx utils with changes + column_widths = column_widths or [] + if wb is None: + wb = openpyxl.Workbook(write_only=True) + + ws = wb.create_sheet(sheet_name, 0) + + for i, column_width in enumerate(column_widths): + if column_width: + ws.column_dimensions[get_column_letter(i + 1)].width = column_width + + row1 = ws.row_dimensions[1] + row1.font = Font(name='Calibri', bold=True) + + for row in data: + clean_row = [] + for item in row: + if isinstance(item, string_types) and (sheet_name not in ['Data Import Template', 'Data Export']): + value = handle_html(item) + else: + value = item + + if isinstance(item, string_types) and next(ILLEGAL_CHARACTERS_RE.finditer(value), None): + # Remove illegal characters from the string + value = re.sub(ILLEGAL_CHARACTERS_RE, '', value) + + clean_row.append(value) + + ws.append(clean_row) + + wb.save(file_path) + return True + +@frappe.whitelist() +def upload_bank_statement(**args): + args = frappe._dict(args) + bsi = frappe.new_doc("Bank Statement Import") + + if args.company: + bsi.update({ + "company": args.company, + }) + + if args.bank_account: + bsi.update({ + "bank_account": args.bank_account + }) + + return bsi diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js new file mode 100644 index 0000000000..6c754022e6 --- /dev/null +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js @@ -0,0 +1,36 @@ +let imports_in_progress = []; + +frappe.listview_settings['Bank Statement Import'] = { + onload(listview) { + frappe.realtime.on('data_import_progress', data => { + if (!imports_in_progress.includes(data.data_import)) { + imports_in_progress.push(data.data_import); + } + }); + frappe.realtime.on('data_import_refresh', data => { + imports_in_progress = imports_in_progress.filter( + d => d !== data.data_import + ); + listview.refresh(); + }); + }, + get_indicator: function(doc) { + var colors = { + 'Pending': 'orange', + 'Not Started': 'orange', + 'Partial Success': 'orange', + 'Success': 'green', + 'In Progress': 'orange', + 'Error': 'red' + }; + let status = doc.status; + if (imports_in_progress.includes(doc.name)) { + status = 'In Progress'; + } + if (status == 'Pending') { + status = 'Not Started'; + } + return [__(status), colors[status], 'status,=,' + doc.status]; + }, + hide_name_column: true +}; diff --git a/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py new file mode 100644 index 0000000000..cd5831412d --- /dev/null +++ b/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestBankStatementImport(unittest.TestCase): + pass diff --git a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.js b/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.js deleted file mode 100644 index 46aa4f2031..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2017, sathishpy@gmail.com and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Bank Statement Settings', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.json b/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.json deleted file mode 100644 index 53fbf7d446..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.json +++ /dev/null @@ -1,272 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "beta": 0, - "creation": "2017-11-13 13:38:10.863592", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Bank Account", - "length": 0, - "no_copy": 0, - "options": "Bank", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "'%d/%m/%Y'", - "fieldname": "date_format", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date Format", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "statement_header_mapping", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Statement Header Mapping", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "header_items", - "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": "Statement Headers", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Settings Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transaction_data_mapping", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transaction Data Mapping", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mapped_items", - "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": "Mapped Items", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Transaction Settings Item", - "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 - } - ], - "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-04-07 18:57:04.048423", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Settings", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 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, - "apply_user_permissions": 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": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.py b/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.py deleted file mode 100644 index 6c4dd1b85b..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document - -class BankStatementSettings(Document): - def autoname(self): - self.name = self.bank + "-Statement-Settings" diff --git a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.js b/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.js deleted file mode 100644 index f2381c042e..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Bank Statement Settings", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Bank Statement Settings - () => frappe.tests.make('Bank Statement Settings', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.py b/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.py deleted file mode 100644 index aa7fe83328..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and Contributors -# See license.txt -from __future__ import unicode_literals - -import frappe -import unittest - -class TestBankStatementSettings(unittest.TestCase): - pass diff --git a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.json b/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.json deleted file mode 100644 index 7c93f268f5..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-01-08 00:16:42.762980", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mapped_header", - "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": "Mapped Header", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stmt_header", - "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": "Bank Header", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-01-08 00:19:14.841134", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Settings Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.py b/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.py deleted file mode 100644 index 9438e9a63f..0000000000 --- a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, sathishpy@gmail.com and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document - -class BankStatementSettingsItem(Document): - pass diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.js b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.js deleted file mode 100644 index 736ed35ae1..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.js +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2017, sathishpy@gmail.com and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Bank Statement Transaction Entry', { - setup: function(frm) { - frm.events.account_filters(frm) - frm.events.invoice_filter(frm) - }, - refresh: function(frm) { - frm.set_df_property("bank_account", "read_only", frm.doc.__islocal ? 0 : 1); - frm.set_df_property("from_date", "read_only", frm.doc.__islocal ? 0 : 1); - frm.set_df_property("to_date", "read_only", frm.doc.__islocal ? 0 : 1); - }, - invoke_doc_function(frm, method) { - frappe.call({ - doc: frm.doc, - method: method, - callback: function(r) { - if(!r.exe) { - frm.refresh_fields(); - } - } - }); - }, - account_filters: function(frm) { - frm.fields_dict['bank_account'].get_query = function(doc, dt, dn) { - return { - filters:[ - ["Account", "account_type", "in", ["Bank"]] - ] - } - }; - frm.fields_dict['receivable_account'].get_query = function(doc, dt, dn) { - return { - filters: {"account_type": "Receivable"} - } - }; - frm.fields_dict['payable_account'].get_query = function(doc, dt, dn) { - return { - filters: {"account_type": "Payable"} - } - }; - }, - - invoice_filter: function(frm) { - frm.set_query("invoice", "payment_invoice_items", function(doc, cdt, cdn) { - let row = locals[cdt][cdn] - if (row.party_type == "Customer") { - return { - filters:[[row.invoice_type, "customer", "in", [row.party]], - [row.invoice_type, "status", "!=", "Cancelled" ], - [row.invoice_type, "posting_date", "<", row.transaction_date ], - [row.invoice_type, "outstanding_amount", ">", 0 ]] - } - } else if (row.party_type == "Supplier") { - return { - filters:[[row.invoice_type, "supplier", "in", [row.party]], - [row.invoice_type, "status", "!=", "Cancelled" ], - [row.invoice_type, "posting_date", "<", row.transaction_date ], - [row.invoice_type, "outstanding_amount", ">", 0 ]] - } - } - }); - }, - - match_invoices: function(frm) { - frm.events.invoke_doc_function(frm, "populate_matching_invoices"); - }, - create_payments: function(frm) { - frm.events.invoke_doc_function(frm, "create_payment_entries"); - }, - submit_payments: function(frm) { - frm.events.invoke_doc_function(frm, "submit_payment_entries"); - }, -}); - - -frappe.ui.form.on('Bank Statement Transaction Invoice Item', { - party_type: function(frm, cdt, cdn) { - let row = locals[cdt][cdn]; - if (row.party_type == "Customer") { - row.invoice_type = "Sales Invoice"; - } else if (row.party_type == "Supplier") { - row.invoice_type = "Purchase Invoice"; - } else if (row.party_type == "Account") { - row.invoice_type = "Journal Entry"; - } - refresh_field("invoice_type", row.name, "payment_invoice_items"); - - }, - invoice_type: function(frm, cdt, cdn) { - let row = locals[cdt][cdn]; - if (row.invoice_type == "Purchase Invoice") { - row.party_type = "Supplier"; - } else if (row.invoice_type == "Sales Invoice") { - row.party_type = "Customer"; - } - refresh_field("party_type", row.name, "payment_invoice_items"); - } -}); \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.json b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.json deleted file mode 100644 index fb80169c37..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.json +++ /dev/null @@ -1,792 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "beta": 0, - "creation": "2017-11-07 13:48:13.123185", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Bank Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "from_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": "From Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "to_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": "To Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_settings", - "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": "Bank Statement Settings", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Settings", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank", - "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": "Bank", - "length": 0, - "no_copy": 0, - "options": "Bank", - "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": "receivable_account", - "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": "Receivable Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payable_account", - "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": "Payable Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_statement", - "fieldtype": "Attach", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Statement", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Transaction Entries", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "new_transaction_items", - "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": "New Transactions", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Transaction Payment Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.new_transaction_items && doc.new_transaction_items.length", - "fieldname": "section_break_9", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "match_invoices", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Match Transaction to Invoices", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_14", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "create_payments", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Create New Payment/Journal Entry", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_16", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "submit_payments", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Submit/Reconcile Payments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.new_transaction_items && doc.new_transaction_items.length", - "fieldname": "section_break_18", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Matching Invoices", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_invoice_items", - "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": "Payment Invoice Items", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Transaction Invoice Item", - "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": "reconciled_transactions", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reconciled Transactions", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reconciled_transaction_items", - "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": "Reconciled Transactions", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Transaction Payment Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Bank Statement Transaction Entry", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-09-14 18:04:44.170455", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Transaction Entry", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py deleted file mode 100644 index 27dd8e463f..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py +++ /dev/null @@ -1,443 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.model.document import Document -from erpnext.accounts.utils import get_outstanding_invoices -from frappe.utils import nowdate -from datetime import datetime -import csv, os, re, io -import difflib -import copy - -class BankStatementTransactionEntry(Document): - def autoname(self): - self.name = self.bank_account + "-" + self.from_date + "-" + self.to_date - if self.bank: - mapper_name = self.bank + "-Statement-Settings" - if not frappe.db.exists("Bank Statement Settings", mapper_name): - self.create_settings(self.bank) - self.bank_settings = mapper_name - - def create_settings(self, bank): - mapper = frappe.new_doc("Bank Statement Settings") - mapper.bank = bank - mapper.date_format = "%Y-%m-%d" - mapper.bank_account = self.bank_account - for header in ["Date", "Particulars", "Withdrawals", "Deposits", "Balance"]: - header_item = mapper.append("header_items", {}) - header_item.mapped_header = header_item.stmt_header = header - mapper.save() - - def on_update(self): - if (not self.bank_statement): - self.reconciled_transaction_items = self.new_transaction_items = [] - return - - if len(self.new_transaction_items + self.reconciled_transaction_items) == 0: - self.populate_payment_entries() - else: - self.match_invoice_to_payment() - - def validate(self): - if not self.new_transaction_items: - self.populate_payment_entries() - - def get_statement_headers(self): - if not self.bank_settings: - frappe.throw(_("Bank Data mapper doesn't exist")) - mapper_doc = frappe.get_doc("Bank Statement Settings", self.bank_settings) - headers = {entry.mapped_header:entry.stmt_header for entry in mapper_doc.header_items} - return headers - - def populate_payment_entries(self): - if self.bank_statement is None: return - file_url = self.bank_statement - if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0): - frappe.throw(_("Transactions already retreived from the statement")) - - date_format = frappe.get_value("Bank Statement Settings", self.bank_settings, "date_format") - if (date_format is None): - date_format = '%Y-%m-%d' - if self.bank_settings: - mapped_items = frappe.get_doc("Bank Statement Settings", self.bank_settings).mapped_items - statement_headers = self.get_statement_headers() - transactions = get_transaction_entries(file_url, statement_headers) - for entry in transactions: - date = entry[statement_headers["Date"]].strip() - #print("Processing entry DESC:{0}-W:{1}-D:{2}-DT:{3}".format(entry["Particulars"], entry["Withdrawals"], entry["Deposits"], entry["Date"])) - if (not date): continue - transaction_date = datetime.strptime(date, date_format).date() - if (self.from_date and transaction_date < datetime.strptime(self.from_date, '%Y-%m-%d').date()): continue - if (self.to_date and transaction_date > datetime.strptime(self.to_date, '%Y-%m-%d').date()): continue - bank_entry = self.append('new_transaction_items', {}) - bank_entry.transaction_date = transaction_date - bank_entry.description = entry[statement_headers["Particulars"]] - - mapped_item = next((entry for entry in mapped_items if entry.mapping_type == "Transaction" and frappe.safe_decode(entry.bank_data.lower()) in frappe.safe_decode(bank_entry.description.lower())), None) - if (mapped_item is not None): - bank_entry.party_type = mapped_item.mapped_data_type - bank_entry.party = mapped_item.mapped_data - else: - bank_entry.party_type = "Supplier" if not entry[statement_headers["Deposits"]].strip() else "Customer" - party_list = frappe.get_all(bank_entry.party_type, fields=["name"]) - parties = [party.name for party in party_list] - matches = difflib.get_close_matches(frappe.safe_decode(bank_entry.description.lower()), parties, 1, 0.4) - if len(matches) > 0: bank_entry.party = matches[0] - bank_entry.amount = -float(entry[statement_headers["Withdrawals"]]) if not entry[statement_headers["Deposits"]].strip() else float(entry[statement_headers["Deposits"]]) - self.map_unknown_transactions() - self.map_transactions_on_journal_entry() - - def map_transactions_on_journal_entry(self): - for entry in self.new_transaction_items: - vouchers = frappe.db.sql("""select name, posting_date from `tabJournal Entry` - where posting_date='{0}' and total_credit={1} and cheque_no='{2}' and docstatus != 2 - """.format(entry.transaction_date, abs(entry.amount), frappe.safe_decode(entry.description)), as_dict=True) - if (len(vouchers) == 1): - entry.reference_name = vouchers[0].name - - def populate_matching_invoices(self): - self.payment_invoice_items = [] - self.map_unknown_transactions() - added_invoices = [] - for entry in self.new_transaction_items: - if (not entry.party or entry.party_type == "Account"): continue - account = self.receivable_account if entry.party_type == "Customer" else self.payable_account - invoices = get_outstanding_invoices(entry.party_type, entry.party, account) - transaction_date = datetime.strptime(entry.transaction_date, "%Y-%m-%d").date() - outstanding_invoices = [invoice for invoice in invoices if invoice.posting_date <= transaction_date] - amount = abs(entry.amount) - matching_invoices = [invoice for invoice in outstanding_invoices if invoice.outstanding_amount == amount] - sorted(outstanding_invoices, key=lambda k: k['posting_date']) - for e in (matching_invoices + outstanding_invoices): - added = next((inv for inv in added_invoices if inv == e.get('voucher_no')), None) - if (added is not None): continue - ent = self.append('payment_invoice_items', {}) - ent.transaction_date = entry.transaction_date - ent.payment_description = frappe.safe_decode(entry.description) - ent.party_type = entry.party_type - ent.party = entry.party - ent.invoice = e.get('voucher_no') - added_invoices += [ent.invoice] - ent.invoice_type = "Sales Invoice" if entry.party_type == "Customer" else "Purchase Invoice" - ent.invoice_date = e.get('posting_date') - ent.outstanding_amount = e.get('outstanding_amount') - ent.allocated_amount = min(float(e.get('outstanding_amount')), amount) - amount -= float(e.get('outstanding_amount')) - if (amount <= 5): break - self.match_invoice_to_payment() - self.populate_matching_vouchers() - self.map_transactions_on_journal_entry() - - def match_invoice_to_payment(self): - added_payments = [] - for entry in self.new_transaction_items: - if (not entry.party or entry.party_type == "Account"): continue - entry.account = self.receivable_account if entry.party_type == "Customer" else self.payable_account - amount = abs(entry.amount) - payment, matching_invoices = None, [] - for inv_entry in self.payment_invoice_items: - if (inv_entry.payment_description != frappe.safe_decode(entry.description) or inv_entry.transaction_date != entry.transaction_date): continue - if (inv_entry.party != entry.party): continue - matching_invoices += [inv_entry.invoice_type + "|" + inv_entry.invoice] - payment = get_payments_matching_invoice(inv_entry.invoice, entry.amount, entry.transaction_date) - doc = frappe.get_doc(inv_entry.invoice_type, inv_entry.invoice) - inv_entry.invoice_date = doc.posting_date - inv_entry.outstanding_amount = doc.outstanding_amount - inv_entry.allocated_amount = min(float(doc.outstanding_amount), amount) - amount -= inv_entry.allocated_amount - if (amount < 0): break - - amount = abs(entry.amount) - if (payment is None): - order_doctype = "Sales Order" if entry.party_type=="Customer" else "Purchase Order" - from erpnext.controllers.accounts_controller import get_advance_payment_entries - payment_entries = get_advance_payment_entries(entry.party_type, entry.party, entry.account, order_doctype, against_all_orders=True) - payment_entries += self.get_matching_payments(entry.party, amount, entry.transaction_date) - payment = next((payment for payment in payment_entries if payment.amount == amount and payment not in added_payments), None) - if (payment is None): - print("Failed to find payments for {0}:{1}".format(entry.party, amount)) - continue - added_payments += [payment] - entry.reference_type = payment.reference_type - entry.reference_name = payment.reference_name - entry.mode_of_payment = "Wire Transfer" - entry.outstanding_amount = min(amount, 0) - if (entry.payment_reference is None): - entry.payment_reference = frappe.safe_decode(entry.description) - entry.invoices = ",".join(matching_invoices) - #print("Matching payment is {0}:{1}".format(entry.reference_type, entry.reference_name)) - - def get_matching_payments(self, party, amount, pay_date): - query = """select 'Payment Entry' as reference_type, name as reference_name, paid_amount as amount - from `tabPayment Entry` where party='{0}' and paid_amount={1} and posting_date='{2}' and docstatus != 2 - """.format(party, amount, pay_date) - matching_payments = frappe.db.sql(query, as_dict=True) - return matching_payments - - def map_unknown_transactions(self): - for entry in self.new_transaction_items: - if (entry.party): continue - inv_type = "Sales Invoice" if (entry.amount > 0) else "Purchase Invoice" - party_type = "customer" if (entry.amount > 0) else "supplier" - - query = """select posting_date, name, {0}, outstanding_amount - from `tab{1}` where ROUND(outstanding_amount)={2} and posting_date < '{3}' - """.format(party_type, inv_type, round(abs(entry.amount)), entry.transaction_date) - invoices = frappe.db.sql(query, as_dict = True) - if(len(invoices) > 0): - entry.party = invoices[0].get(party_type) - - def populate_matching_vouchers(self): - for entry in self.new_transaction_items: - if (not entry.party or entry.reference_name): continue - print("Finding matching voucher for {0}".format(frappe.safe_decode(entry.description))) - amount = abs(entry.amount) - invoices = [] - vouchers = get_matching_journal_entries(self.from_date, self.to_date, entry.party, self.bank_account, amount) - if len(vouchers) == 0: continue - for voucher in vouchers: - added = next((entry.invoice for entry in self.payment_invoice_items if entry.invoice == voucher.voucher_no), None) - if (added): - print("Found voucher {0}".format(added)) - continue - print("Adding voucher {0} {1} {2}".format(voucher.voucher_no, voucher.posting_date, voucher.debit)) - ent = self.append('payment_invoice_items', {}) - ent.invoice_date = voucher.posting_date - ent.invoice_type = "Journal Entry" - ent.invoice = voucher.voucher_no - ent.payment_description = frappe.safe_decode(entry.description) - ent.allocated_amount = max(voucher.debit, voucher.credit) - - invoices += [ent.invoice_type + "|" + ent.invoice] - entry.reference_type = "Journal Entry" - entry.mode_of_payment = "Wire Transfer" - entry.reference_name = ent.invoice - #entry.account = entry.party - entry.invoices = ",".join(invoices) - break - - - def create_payment_entries(self): - for payment_entry in self.new_transaction_items: - if (not payment_entry.party): continue - if (payment_entry.reference_name): continue - print("Creating payment entry for {0}".format(frappe.safe_decode(payment_entry.description))) - if (payment_entry.party_type == "Account"): - payment = self.create_journal_entry(payment_entry) - invoices = [payment.doctype + "|" + payment.name] - payment_entry.invoices = ",".join(invoices) - else: - payment = self.create_payment_entry(payment_entry) - invoices = [entry.reference_doctype + "|" + entry.reference_name for entry in payment.references if entry is not None] - payment_entry.invoices = ",".join(invoices) - payment_entry.mode_of_payment = payment.mode_of_payment - payment_entry.account = self.receivable_account if payment_entry.party_type == "Customer" else self.payable_account - payment_entry.reference_name = payment.name - payment_entry.reference_type = payment.doctype - frappe.msgprint(_("Successfully created payment entries")) - - def create_payment_entry(self, pe): - payment = frappe.new_doc("Payment Entry") - payment.posting_date = pe.transaction_date - payment.payment_type = "Receive" if pe.party_type == "Customer" else "Pay" - payment.mode_of_payment = "Wire Transfer" - payment.party_type = pe.party_type - payment.party = pe.party - payment.paid_to = self.bank_account if pe.party_type == "Customer" else self.payable_account - payment.paid_from = self.receivable_account if pe.party_type == "Customer" else self.bank_account - payment.paid_amount = payment.received_amount = abs(pe.amount) - payment.reference_no = pe.description - payment.reference_date = pe.transaction_date - payment.save() - for inv_entry in self.payment_invoice_items: - if (pe.description != inv_entry.payment_description or pe.transaction_date != inv_entry.transaction_date): continue - if (pe.party != inv_entry.party): continue - reference = payment.append("references", {}) - reference.reference_doctype = inv_entry.invoice_type - reference.reference_name = inv_entry.invoice - reference.allocated_amount = inv_entry.allocated_amount - print ("Adding invoice {0} {1}".format(reference.reference_name, reference.allocated_amount)) - payment.setup_party_account_field() - payment.set_missing_values() - #payment.set_exchange_rate() - #payment.set_amounts() - #print("Created payment entry {0}".format(payment.as_dict())) - payment.save() - return payment - - def create_journal_entry(self, pe): - je = frappe.new_doc("Journal Entry") - je.is_opening = "No" - je.voucher_type = "Bank Entry" - je.cheque_no = pe.description - je.cheque_date = pe.transaction_date - je.remark = pe.description - je.posting_date = pe.transaction_date - if (pe.amount < 0): - je.append("accounts", {"account": pe.party, "debit_in_account_currency": abs(pe.amount)}) - je.append("accounts", {"account": self.bank_account, "credit_in_account_currency": abs(pe.amount)}) - else: - je.append("accounts", {"account": pe.party, "credit_in_account_currency": pe.amount}) - je.append("accounts", {"account": self.bank_account, "debit_in_account_currency": pe.amount}) - je.save() - return je - - def update_payment_entry(self, payment): - lst = [] - invoices = payment.invoices.strip().split(',') - if (len(invoices) == 0): return - amount = float(abs(payment.amount)) - for invoice_entry in invoices: - if (not invoice_entry.strip()): continue - invs = invoice_entry.split('|') - invoice_type, invoice = invs[0], invs[1] - outstanding_amount = frappe.get_value(invoice_type, invoice, 'outstanding_amount') - - lst.append(frappe._dict({ - 'voucher_type': payment.reference_type, - 'voucher_no' : payment.reference_name, - 'against_voucher_type' : invoice_type, - 'against_voucher' : invoice, - 'account' : payment.account, - 'party_type': payment.party_type, - 'party': frappe.get_value("Payment Entry", payment.reference_name, "party"), - 'unadjusted_amount' : float(amount), - 'allocated_amount' : min(outstanding_amount, amount) - })) - amount -= outstanding_amount - if lst: - from erpnext.accounts.utils import reconcile_against_document - try: - reconcile_against_document(lst) - except: - frappe.throw(_("Exception occurred while reconciling {0}").format(payment.reference_name)) - - def submit_payment_entries(self): - for payment in self.new_transaction_items: - if payment.reference_name is None: continue - doc = frappe.get_doc(payment.reference_type, payment.reference_name) - if doc.docstatus == 1: - if (payment.reference_type == "Journal Entry"): continue - if doc.unallocated_amount == 0: continue - print("Reconciling payment {0}".format(payment.reference_name)) - self.update_payment_entry(payment) - else: - print("Submitting payment {0}".format(payment.reference_name)) - if (payment.reference_type == "Payment Entry"): - if (payment.payment_reference): - doc.reference_no = payment.payment_reference - doc.mode_of_payment = payment.mode_of_payment - doc.save() - doc.submit() - self.move_reconciled_entries() - self.populate_matching_invoices() - - def move_reconciled_entries(self): - idx = 0 - while idx < len(self.new_transaction_items): - entry = self.new_transaction_items[idx] - try: - print("Checking transaction {0}: {2} in {1} entries".format(idx, len(self.new_transaction_items), frappe.safe_decode(entry.description))) - except UnicodeEncodeError: - pass - idx += 1 - if entry.reference_name is None: continue - doc = frappe.get_doc(entry.reference_type, entry.reference_name) - if doc.docstatus == 1 and (entry.reference_type == "Journal Entry" or doc.unallocated_amount == 0): - self.remove(entry) - rc_entry = self.append('reconciled_transaction_items', {}) - dentry = entry.as_dict() - dentry.pop('idx', None) - rc_entry.update(dentry) - idx -= 1 - - -def get_matching_journal_entries(from_date, to_date, account, against, amount): - query = """select voucher_no, posting_date, account, against, debit_in_account_currency as debit, credit_in_account_currency as credit - from `tabGL Entry` - where posting_date between '{0}' and '{1}' and account = '{2}' and against = '{3}' and debit = '{4}' - """.format(from_date, to_date, account, against, amount) - jv_entries = frappe.db.sql(query, as_dict=True) - #print("voucher query:{0}\n Returned {1} entries".format(query, len(jv_entries))) - return jv_entries - -def get_payments_matching_invoice(invoice, amount, pay_date): - query = """select pe.name as reference_name, per.reference_doctype as reference_type, per.outstanding_amount, per.allocated_amount - from `tabPayment Entry Reference` as per JOIN `tabPayment Entry` as pe on pe.name = per.parent - where per.reference_name='{0}' and (posting_date='{1}' or reference_date='{1}') and pe.docstatus != 2 - """.format(invoice, pay_date) - payments = frappe.db.sql(query, as_dict=True) - if (len(payments) == 0): return - payment = next((payment for payment in payments if payment.allocated_amount == amount), payments[0]) - #Hack: Update the reference type which is set to invoice type - payment.reference_type = "Payment Entry" - return payment - -def is_headers_present(headers, row): - for header in headers: - if header not in row: - return False - return True - -def get_header_index(headers, row): - header_index = {} - for header in headers: - if header in row: - header_index[header] = row.index(header) - return header_index - -def get_transaction_info(headers, header_index, row): - transaction = {} - for header in headers: - transaction[header] = row[header_index[header]] - if (transaction[header] == None): - transaction[header] = "" - return transaction - -def get_transaction_entries(file_url, headers): - header_index = {} - rows, transactions = [], [] - - if (file_url.lower().endswith("xlsx")): - from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file - rows = read_xlsx_file_from_attached_file(file_url=file_url) - elif (file_url.lower().endswith("csv")): - from frappe.utils.csvutils import read_csv_content - _file = frappe.get_doc("File", {"file_url": file_url}) - filepath = _file.get_full_path() - with open(filepath,'rb') as csvfile: - rows = read_csv_content(csvfile.read()) - elif (file_url.lower().endswith("xls")): - filename = file_url.split("/")[-1] - rows = get_rows_from_xls_file(filename) - else: - frappe.throw(_("Only .csv and .xlsx files are supported currently")) - - stmt_headers = headers.values() - for row in rows: - if len(row) == 0 or row[0] == None or not row[0]: continue - #print("Processing row {0}".format(row)) - if header_index: - transaction = get_transaction_info(stmt_headers, header_index, row) - transactions.append(transaction) - elif is_headers_present(stmt_headers, row): - header_index = get_header_index(stmt_headers, row) - return transactions - -def get_rows_from_xls_file(filename): - _file = frappe.get_doc("File", {"file_name": filename}) - filepath = _file.get_full_path() - import xlrd - book = xlrd.open_workbook(filepath) - sheets = book.sheets() - rows = [] - for row in range(1, sheets[0].nrows): - row_values = [] - for col in range(1, sheets[0].ncols): - row_values.append(sheets[0].cell_value(row, col)) - rows.append(row_values) - return rows diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.js b/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.js deleted file mode 100644 index 46d570f515..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Bank Statement Transaction Entry", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Bank Statement Transaction Entry - () => frappe.tests.make('Bank Statement Transaction Entry', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.py deleted file mode 100644 index 458948372f..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and Contributors -# See license.txt -from __future__ import unicode_literals - -import frappe -import unittest - -class TestBankStatementTransactionEntry(unittest.TestCase): - pass diff --git a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.json b/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.json deleted file mode 100644 index d96c94d8ca..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.json +++ /dev/null @@ -1,365 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-07 13:58:53.827058", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transaction_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transaction Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 4, - "fieldname": "payment_description", - "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": "Payment Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "party_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Party Type", - "length": 0, - "no_copy": 0, - "options": "Customer\nSupplier\nAccount", - "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": "party", - "fieldtype": "Dynamic 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": "Party", - "length": 0, - "no_copy": 0, - "options": "party_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "invoice_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": "Invoice Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "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": "invoice_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Invoice Type", - "length": 0, - "no_copy": 0, - "options": "Sales Invoice\nPurchase Invoice\nJournal Entry", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "invoice", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "invoice", - "length": 0, - "no_copy": 0, - "options": "invoice_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 1, - "fieldname": "outstanding_amount", - "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": "Outstanding Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 1, - "fieldname": "allocated_amount", - "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": "Allocated Amount", - "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 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-09-14 19:03:30.949831", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Transaction Invoice Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.json b/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.json deleted file mode 100644 index 177dccd82c..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.json +++ /dev/null @@ -1,494 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-07 14:03:05.651413", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 1, - "fieldname": "transaction_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": "Transaction Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 4, - "fieldname": "description", - "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": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 1, - "fieldname": "amount", - "fieldtype": "Currency", - "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": "Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 1, - "fieldname": "party_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Party Type", - "length": 0, - "no_copy": 0, - "options": "Customer\nSupplier\nAccount", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "party", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Party", - "length": 0, - "no_copy": 0, - "options": "party_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Type", - "length": 0, - "no_copy": 0, - "options": "Payment Entry\nJournal Entry", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "account", - "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": "Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mode_of_payment", - "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": "Mode of Payment", - "length": 0, - "no_copy": 0, - "options": "Mode of Payment", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "outstanding_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "outstanding_amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_10", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "reference_name", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reference Name", - "length": 0, - "no_copy": 0, - "options": "reference_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_reference", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Reference", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "invoices", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Invoices", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2017-11-15 19:18:52.876221", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Transaction Payment Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.js b/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.js deleted file mode 100644 index 46aa4f2031..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2017, sathishpy@gmail.com and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Bank Statement Settings', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.json b/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.json deleted file mode 100644 index 474bb90db7..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.json +++ /dev/null @@ -1,266 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "beta": 0, - "creation": "2017-11-13 13:38:10.863592", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Bank Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "'%d/%m/%Y'", - "fieldname": "date_format", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date Format", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "statement_header_mapping", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Statement Header Mapping", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "header_items", - "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": "Statement Headers", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Settings Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transaction_data_mapping", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transaction Data Mapping", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mapped_items", - "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": "Mapped Items", - "length": 0, - "no_copy": 0, - "options": "Bank Statement Transaction Settings Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "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-01-12 10:34:32.840487", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Settings", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 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, - "apply_user_permissions": 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": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.py b/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.py deleted file mode 100644 index de9a85fe5c..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document - -class BankStatementSettings(Document): - def autoname(self): - self.name = self.bank_account + "-Mappings" diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.js b/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.js deleted file mode 100644 index f2381c042e..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Bank Statement Settings", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Bank Statement Settings - () => frappe.tests.make('Bank Statement Settings', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.py b/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.py deleted file mode 100644 index aa7fe83328..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and Contributors -# See license.txt -from __future__ import unicode_literals - -import frappe -import unittest - -class TestBankStatementSettings(unittest.TestCase): - pass diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_settings_item/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.json b/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.json deleted file mode 100644 index 47c32097a9..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-13 13:42:00.335432", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Transaction", - "fieldname": "mapping_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Mapping Type", - "length": 0, - "no_copy": 0, - "options": "Transaction", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_data", - "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": "Bank Data", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Account", - "fieldname": "mapped_data_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Mapped Data Type", - "length": 0, - "no_copy": 0, - "options": "Account\nCustomer\nSupplier\nAccount", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mapped_data", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Mapped Data", - "length": 0, - "no_copy": 0, - "options": "mapped_data_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-01-08 00:13:49.973501", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Statement Transaction Settings Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.py b/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.py deleted file mode 100644 index bf0a590d48..0000000000 --- a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document - -class BankStatementTransactionSettingsItem(Document): - pass diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.js b/erpnext/accounts/doctype/bank_transaction/bank_transaction.js index 8b1bab1618..3758b524da 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.js +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.js @@ -1,32 +1,70 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Bank Transaction', { +frappe.ui.form.on("Bank Transaction", { onload(frm) { - frm.set_query('payment_document', 'payment_entries', function() { + frm.set_query("payment_document", "payment_entries", function () { return { - "filters": { - "name": ["in", ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Expense Claim"]] - } + filters: { + name: [ + "in", + [ + "Payment Entry", + "Journal Entry", + "Sales Invoice", + "Purchase Invoice", + "Expense Claim", + ], + ], + }, }; }); - } + }, + bank_account: function (frm) { + set_bank_statement_filter(frm); + }, + + setup: function (frm) { + frm.set_query("party_type", function () { + return { + filters: { + name: ["in", Object.keys(frappe.boot.party_account_types)], + }, + }; + }); + }, }); -frappe.ui.form.on('Bank Transaction Payments', { - payment_entries_remove: function(frm, cdt, cdn) { +frappe.ui.form.on("Bank Transaction Payments", { + payment_entries_remove: function (frm, cdt, cdn) { update_clearance_date(frm, cdt, cdn); - } + }, }); const update_clearance_date = (frm, cdt, cdn) => { if (frm.doc.docstatus === 1) { - frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment', - {doctype: cdt, docname: cdn}) - .then(e => { + frappe + .xcall( + "erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment", + { doctype: cdt, docname: cdn } + ) + .then((e) => { if (e == "success") { - frappe.show_alert({message:__("Document {0} successfully uncleared", [e]), indicator:'green'}); + frappe.show_alert({ + message: __("Document {0} successfully uncleared", [e]), + indicator: "green", + }); } }); } -}; \ No newline at end of file +}; + +function set_bank_statement_filter(frm) { + frm.set_query("bank_statement", function () { + return { + filters: { + bank_account: frm.doc.bank_account, + }, + }; + }); +} diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json index 39937bb364..69ee4971cd 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json @@ -1,833 +1,245 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, - "allow_rename": 0, "autoname": "naming_series:", - "beta": 0, "creation": "2018-10-22 18:19:02.784533", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "naming_series", + "date", + "column_break_2", + "status", + "bank_account", + "company", + "section_break_4", + "deposit", + "withdrawal", + "column_break_7", + "currency", + "section_break_10", + "description", + "section_break_14", + "reference_number", + "transaction_id", + "payment_entries", + "section_break_18", + "allocated_amount", + "amended_from", + "column_break_17", + "unallocated_amount", + "party_section", + "party_type", + "party" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "ACC-BTN-.YYYY.-", - "fetch_if_empty": 0, "fieldname": "naming_series", "fieldtype": "Select", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Series", - "length": 0, "no_copy": 1, "options": "ACC-BTN-.YYYY.-", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Pending", - "fetch_if_empty": 0, "fieldname": "status", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Status", - "length": 0, - "no_copy": 0, - "options": "\nPending\nSettled\nUnreconciled\nReconciled", - "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": "\nPending\nSettled\nUnreconciled\nReconciled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "bank_account", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Bank Account", - "length": 0, - "no_copy": 0, - "options": "Bank Account", - "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": "Bank Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", "fetch_from": "bank_account.company", - "fetch_if_empty": 0, "fieldname": "company", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Company", - "length": 0, - "no_copy": 0, "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "debit", - "fieldtype": "Currency", - "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": "Debit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "credit", - "fieldtype": "Currency", - "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": "Credit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_7", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "currency", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "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": "Currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "section_break_10", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "description", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Description" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "section_break_14", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "allow_on_submit": 1, "fieldname": "reference_number", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Number", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Reference Number" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "transaction_id", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Transaction ID", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, "unique": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "payment_entries", "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": "Payment Entries", - "length": 0, - "no_copy": 0, - "options": "Bank Transaction Payments", - "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": "Bank Transaction Payments" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "section_break_18", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "allocated_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allocated Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Allocated Amount" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Amended From", - "length": 0, "no_copy": 1, "options": "Bank Transaction", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_17", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "unallocated_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Unallocated Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Unallocated Amount" + }, + { + "fieldname": "party_section", + "fieldtype": "Section Break", + "label": "Payment From / To" + }, + { + "allow_on_submit": 1, + "fieldname": "party_type", + "fieldtype": "Link", + "label": "Party Type", + "options": "DocType" + }, + { + "allow_on_submit": 1, + "fieldname": "party", + "fieldtype": "Dynamic Link", + "label": "Party", + "options": "party_type" + }, + { + "fieldname": "deposit", + "oldfieldname": "debit", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Deposit" + }, + { + "fieldname": "withdrawal", + "oldfieldname": "credit", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Withdrawal" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-05-11 05:27:55.244721", + "links": [], + "modified": "2020-12-30 19:40:54.221070", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Transaction", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, "cancel": 1, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 }, { - "amend": 0, "cancel": 1, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Accounts Manager", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Accounts User", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, "sort_field": "date", "sort_order": "DESC", "title_field": "bank_account", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 0e45db3dbc..5246baa02b 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -11,7 +11,7 @@ from frappe import _ class BankTransaction(StatusUpdater): def after_insert(self): - self.unallocated_amount = abs(flt(self.credit) - flt(self.debit)) + self.unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit)) def on_submit(self): self.clear_linked_payment_entries() @@ -30,13 +30,13 @@ class BankTransaction(StatusUpdater): if allocated_amount: frappe.db.set_value(self.doctype, self.name, "allocated_amount", flt(allocated_amount)) - frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)) - flt(allocated_amount)) + frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)) - flt(allocated_amount)) else: frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0) - frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit))) + frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit))) - amount = self.debit or self.credit + amount = self.deposit or self.withdrawal if amount == self.allocated_amount: frappe.db.set_value(self.doctype, self.name, "status", "Reconciled") @@ -44,18 +44,11 @@ class BankTransaction(StatusUpdater): def clear_linked_payment_entries(self): for payment_entry in self.payment_entries: - allocated_amount = get_total_allocated_amount(payment_entry) - paid_amount = get_paid_amount(payment_entry, self.currency) + if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]: + self.clear_simple_entry(payment_entry) - if paid_amount and allocated_amount: - if flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount): - frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).").format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount))) - else: - if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]: - self.clear_simple_entry(payment_entry) - - elif payment_entry.payment_document == "Sales Invoice": - self.clear_sales_invoice(payment_entry) + elif payment_entry.payment_document == "Sales Invoice": + self.clear_sales_invoice(payment_entry) def clear_simple_entry(self, payment_entry): frappe.db.set_value(payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", self.date) @@ -112,3 +105,4 @@ def unclear_reference_payment(doctype, docname): frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None) return doc.payment_entry + diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py index e9fc5f0a1d..3b14e4efa0 100644 --- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py @@ -5,10 +5,11 @@ from __future__ import unicode_literals import frappe import unittest +import json 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.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry -from erpnext.accounts.page.bank_reconciliation.bank_reconciliation import reconcile, get_linked_payments +from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import reconcile_vouchers, get_linked_payments from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile test_dependencies = ["Item", "Cost Center"] @@ -17,7 +18,7 @@ class TestBankTransaction(unittest.TestCase): def setUp(self): make_pos_profile() add_transactions() - add_payments() + add_vouchers() def tearDown(self): for bt in frappe.get_all("Bank Transaction"): @@ -38,14 +39,18 @@ class TestBankTransaction(unittest.TestCase): # This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction. def test_linked_payments(self): bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic")) - linked_payments = get_linked_payments(bank_transaction.name) - self.assertTrue(linked_payments[0].party == "Conrad Electronic") + linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match']) + self.assertTrue(linked_payments[0][6] == "Conrad Electronic") # This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment def test_reconcile(self): bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G")) payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200)) - reconcile(bank_transaction.name, "Payment Entry", payment.name) + vouchers = json.dumps([{ + "payment_doctype":"Payment Entry", + "payment_name":payment.name, + "amount":bank_transaction.unallocated_amount}]) + reconcile_vouchers(bank_transaction.name, vouchers) unallocated_amount = frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount") self.assertTrue(unallocated_amount == 0) @@ -53,45 +58,40 @@ class TestBankTransaction(unittest.TestCase): clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date") self.assertTrue(clearance_date is not None) - # Check if ERPNext can correctly fetch a linked payment based on the party - def test_linked_payments_based_on_party(self): - bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G")) - linked_payments = get_linked_payments(bank_transaction.name) - self.assertTrue(len(linked_payments)==1) - # Check if ERPNext can correctly filter a linked payments based on the debit/credit amount def test_debit_credit_output(self): bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07")) - linked_payments = get_linked_payments(bank_transaction.name) - self.assertTrue(linked_payments[0].payment_type == "Pay") + linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match']) + print(linked_payments) + self.assertTrue(linked_payments[0][3]) # Check error if already reconciled def test_already_reconciled(self): bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G")) payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200)) - reconcile(bank_transaction.name, "Payment Entry", payment.name) + vouchers = json.dumps([{ + "payment_doctype":"Payment Entry", + "payment_name":payment.name, + "amount":bank_transaction.unallocated_amount}]) + reconcile_vouchers(bank_transaction.name, vouchers) bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G")) payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200)) - self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name) - - # Raise an error if creditor transaction vs creditor payment - def test_invalid_creditor_reconcilation(self): - bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio")) - payment = frappe.get_doc("Payment Entry", dict(party="Conrad Electronic", paid_amount=690)) - self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name) - - # Raise an error if debitor transaction vs debitor payment - def test_invalid_debitor_reconcilation(self): - bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07")) - payment = frappe.get_doc("Payment Entry", dict(party="Fayva", paid_amount=109080)) - self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name) + vouchers = json.dumps([{ + "payment_doctype":"Payment Entry", + "payment_name":payment.name, + "amount":bank_transaction.unallocated_amount}]) + self.assertRaises(frappe.ValidationError, reconcile_vouchers, bank_transaction_name=bank_transaction.name, vouchers=vouchers) # Raise an error if debitor transaction vs debitor payment def test_clear_sales_invoice(self): bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio")) payment = frappe.get_doc("Sales Invoice", dict(customer="Fayva", status=["=", "Paid"])) - reconcile(bank_transaction.name, "Sales Invoice", payment.name) + vouchers = json.dumps([{ + "payment_doctype":"Sales Invoice", + "payment_name":payment.name, + "amount":bank_transaction.unallocated_amount}]) + reconcile_vouchers(bank_transaction.name, vouchers=vouchers) self.assertEqual(frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0) self.assertTrue(frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date") is not None) @@ -126,7 +126,7 @@ def add_transactions(): "doctype": "Bank Transaction", "description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G", "date": "2018-10-23", - "debit": 1200, + "deposit": 1200, "currency": "INR", "bank_account": "Checking Account - Citi Bank" }).insert() @@ -136,7 +136,7 @@ def add_transactions(): "doctype": "Bank Transaction", "description":"1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G", "date": "2018-10-23", - "debit": 1700, + "deposit": 1700, "currency": "INR", "bank_account": "Checking Account - Citi Bank" }).insert() @@ -146,7 +146,7 @@ def add_transactions(): "doctype": "Bank Transaction", "description":"Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic", "date": "2018-10-26", - "debit": 690, + "withdrawal": 690, "currency": "INR", "bank_account": "Checking Account - Citi Bank" }).insert() @@ -156,7 +156,7 @@ def add_transactions(): "doctype": "Bank Transaction", "description":"Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07", "date": "2018-10-27", - "debit": 3900, + "deposit": 3900, "currency": "INR", "bank_account": "Checking Account - Citi Bank" }).insert() @@ -166,7 +166,7 @@ def add_transactions(): "doctype": "Bank Transaction", "description":"I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio", "date": "2018-10-27", - "credit": 109080, + "withdrawal": 109080, "currency": "INR", "bank_account": "Checking Account - Citi Bank" }).insert() @@ -174,7 +174,7 @@ def add_transactions(): frappe.flags.test_bank_transactions_created = True -def add_payments(): +def add_vouchers(): if frappe.flags.test_payments_created: return @@ -192,6 +192,7 @@ def add_payments(): pass pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690) + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") pe.reference_no = "Conrad Oct 18" pe.reference_date = "2018-10-24" @@ -242,10 +243,15 @@ def add_payments(): except frappe.DuplicateEntryError: pass - pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900) + pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save =1) + pi.cash_bank_account = "_Test Bank - _TC" + pi.insert() + pi.submit() pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") pe.reference_no = "Poore Simon's Oct 18" pe.reference_date = "2018-10-28" + pe.paid_amount = 690 + pe.received_amount = 690 pe.insert() pe.submit() @@ -295,4 +301,4 @@ def add_payments(): si.save() si.submit() - frappe.flags.test_payments_created = True \ No newline at end of file + frappe.flags.test_payments_created = True diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index b0a864f76c..ce76d0a39c 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -27,30 +27,30 @@ class GLEntry(Document): def validate(self): self.flags.ignore_submit_comment = True - self.check_mandatory() self.validate_and_set_fiscal_year() self.pl_must_have_cost_center() - self.validate_cost_center() if not self.flags.from_repost: + self.check_mandatory() + self.validate_cost_center() self.check_pl_account() self.validate_party() self.validate_currency() - def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False): - if not from_repost: + def on_update(self): + adv_adj = self.flags.adv_adj + if not self.flags.from_repost: self.validate_account_details(adv_adj) self.validate_dimensions_for_pl_and_bs() self.validate_allowed_dimensions() + validate_balance_type(self.account, adv_adj) + validate_frozen_account(self.account, adv_adj) - validate_frozen_account(self.account, adv_adj) - validate_balance_type(self.account, adv_adj) - - # Update outstanding amt on against voucher - if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] \ - and self.against_voucher and update_outstanding == 'Yes' and not from_repost: - update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type, - self.against_voucher) + # Update outstanding amt on against voucher + if (self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] + and self.against_voucher and self.flags.update_outstanding == 'Yes'): + update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type, + self.against_voucher) def check_mandatory(self): mandatory = ['account','voucher_type','voucher_no','company'] @@ -58,7 +58,7 @@ class GLEntry(Document): if not self.get(k): frappe.throw(_("{0} is required").format(_(self.meta.get_label(k)))) - account_type = frappe.db.get_value("Account", self.account, "account_type") + account_type = frappe.get_cached_value("Account", self.account, "account_type") if not (self.party_type and self.party): if account_type == "Receivable": frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}") @@ -73,7 +73,7 @@ class GLEntry(Document): .format(self.voucher_type, self.voucher_no, self.account)) def pl_must_have_cost_center(self): - if frappe.db.get_value("Account", self.account, "report_type") == "Profit and Loss": + if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss": if not self.cost_center and self.voucher_type != 'Period Closing Voucher': frappe.throw(_("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}. Please set up a default Cost Center for the Company.") .format(self.voucher_type, self.voucher_no, self.account)) @@ -140,25 +140,16 @@ class GLEntry(Document): .format(self.voucher_type, self.voucher_no, self.account, self.company)) def validate_cost_center(self): - if not hasattr(self, "cost_center_company"): - self.cost_center_company = {} + if not self.cost_center: return - def _get_cost_center_company(): - if not self.cost_center_company.get(self.cost_center): - self.cost_center_company[self.cost_center] = frappe.db.get_value( - "Cost Center", self.cost_center, "company") + is_group, company = frappe.get_cached_value('Cost Center', + self.cost_center, ['is_group', 'company']) - return self.cost_center_company[self.cost_center] - - def _check_is_group(): - return cint(frappe.get_cached_value('Cost Center', self.cost_center, 'is_group')) - - if self.cost_center and _get_cost_center_company() != self.company: + if company != self.company: frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}") .format(self.voucher_type, self.voucher_no, self.cost_center, self.company)) - if not self.flags.from_repost and not self.voucher_type == 'Period Closing Voucher' \ - and self.cost_center and _check_is_group(): + if (self.voucher_type != 'Period Closing Voucher' and is_group): frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""").format( self.voucher_type, self.voucher_no, frappe.bold(self.cost_center))) @@ -184,7 +175,6 @@ class GLEntry(Document): if not self.fiscal_year: self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0] - def validate_balance_type(account, adv_adj=False): if not adv_adj and account: balance_must_be = frappe.db.get_value("Account", account, "balance_must_be") @@ -250,7 +240,7 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga def validate_frozen_account(account, adv_adj=None): - frozen_account = frappe.db.get_value("Account", account, "freeze_account") + frozen_account = frappe.get_cached_value("Account", account, "freeze_account") if frozen_account == 'Yes' and not adv_adj: frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None, 'frozen_accounts_modifier') diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index cb90f8036e..3419bb6c3e 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -102,7 +102,7 @@ class JournalEntry(AccountsController): if account_currency == previous_account_currency: if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit: frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry")) - + def validate_stock_accounts(self): stock_accounts = get_stock_accounts(self.company, self.doctype, self.name) for account in stock_accounts: @@ -229,11 +229,11 @@ class JournalEntry(AccountsController): if d.reference_type=="Journal Entry": account_root_type = frappe.db.get_value("Account", d.account, "root_type") if account_root_type == "Asset" and flt(d.debit) > 0: - frappe.throw(_("For {0}, only credit accounts can be linked against another debit entry") - .format(d.account)) + frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets credited") + .format(d.idx, d.account)) elif account_root_type == "Liability" and flt(d.credit) > 0: - frappe.throw(_("For {0}, only debit accounts can be linked against another credit entry") - .format(d.account)) + frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets debited") + .format(d.idx, d.account)) if d.reference_name == self.name: frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column")) @@ -1077,4 +1077,4 @@ def make_reverse_journal_entry(source_name, target_doc=None): }, }, target_doc) - return doclist \ No newline at end of file + return doclist diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 1b97050eb1..53ac996290 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -3,6 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals +import json import frappe from frappe import _ from frappe.model.document import Document @@ -82,18 +83,37 @@ class PaymentRequest(Document): self.make_communication_entry() elif self.payment_channel == "Phone": - controller = get_payment_gateway_controller(self.payment_gateway) - payment_record = dict( - reference_doctype="Payment Request", - reference_docname=self.name, - payment_reference=self.reference_name, - grand_total=self.grand_total, - sender=self.email_to, - currency=self.currency, - payment_gateway=self.payment_gateway - ) - controller.validate_transaction_currency(self.currency) - controller.request_for_payment(**payment_record) + self.request_phone_payment() + + def request_phone_payment(self): + controller = get_payment_gateway_controller(self.payment_gateway) + request_amount = self.get_request_amount() + + payment_record = dict( + reference_doctype="Payment Request", + reference_docname=self.name, + payment_reference=self.reference_name, + request_amount=request_amount, + sender=self.email_to, + currency=self.currency, + payment_gateway=self.payment_gateway + ) + + controller.validate_transaction_currency(self.currency) + controller.request_for_payment(**payment_record) + + def get_request_amount(self): + data_of_completed_requests = frappe.get_all("Integration Request", filters={ + 'reference_doctype': self.doctype, + 'reference_docname': self.name, + 'status': 'Completed' + }, pluck="data") + + if not data_of_completed_requests: + return self.grand_total + + request_amounts = sum([json.loads(d).get('request_amount') for d in data_of_completed_requests]) + return request_amounts def on_cancel(self): self.check_if_payment_entry_exists() @@ -351,8 +371,8 @@ def make_payment_request(**args): if args.order_type == "Shopping Cart" or args.mute_email: pr.flags.mute_email = True + pr.insert(ignore_permissions=True) if args.submit_doc: - pr.insert(ignore_permissions=True) pr.submit() if args.order_type == "Shopping Cart": @@ -412,8 +432,8 @@ def get_existing_payment_request_amount(ref_dt, ref_dn): def get_gateway_details(args): """return gateway and payment account of default payment gateway""" - if args.get("payment_gateway"): - return get_payment_gateway_account(args.get("payment_gateway")) + if args.get("payment_gateway_account"): + return get_payment_gateway_account(args.get("payment_gateway_account")) if args.order_type == "Shopping Cart": payment_gateway_account = frappe.get_doc("Shopping Cart Settings").payment_gateway_account diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 8a10e2cbd9..5eba62c0b3 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -45,7 +45,8 @@ class TestPaymentRequest(unittest.TestCase): def test_payment_request_linkings(self): so_inr = make_sales_order(currency="INR") - pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com") + pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com", + payment_gateway_account="_Test Gateway - INR") self.assertEqual(pr.reference_doctype, "Sales Order") self.assertEqual(pr.reference_name, so_inr.name) @@ -54,7 +55,8 @@ class TestPaymentRequest(unittest.TestCase): conversion_rate = get_exchange_rate("USD", "INR") si_usd = create_sales_invoice(currency="USD", conversion_rate=conversion_rate) - pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com") + pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com", + payment_gateway_account="_Test Gateway - USD") self.assertEqual(pr.reference_doctype, "Sales Invoice") self.assertEqual(pr.reference_name, si_usd.name) @@ -68,7 +70,7 @@ class TestPaymentRequest(unittest.TestCase): so_inr = make_sales_order(currency="INR") pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com", - mute_email=1, submit_doc=1, return_doc=1) + mute_email=1, payment_gateway_account="_Test Gateway - INR", submit_doc=1, return_doc=1) pe = pr.set_as_paid() so_inr = frappe.get_doc("Sales Order", so_inr.name) @@ -79,7 +81,7 @@ class TestPaymentRequest(unittest.TestCase): currency="USD", conversion_rate=50) pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com", - mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1, return_doc=1) + mute_email=1, payment_gateway_account="_Test Gateway - USD", submit_doc=1, return_doc=1) pe = pr.set_as_paid() @@ -106,7 +108,7 @@ class TestPaymentRequest(unittest.TestCase): currency="USD", conversion_rate=50) pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com", - mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1, return_doc=1) + mute_email=1, payment_gateway_account="_Test Gateway - USD", submit_doc=1, return_doc=1) pe = pr.create_payment_entry() pr.load_from_db() diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js index 73367fd736..9ea616f8e7 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js @@ -21,7 +21,7 @@ frappe.ui.form.on('POS Closing Entry', { return { filters: { 'status': 'Open', 'docstatus': 1 } }; }); - if (frm.doc.docstatus === 0) frm.set_value("period_end_date", frappe.datetime.now_datetime()); + if (frm.doc.docstatus === 0 && !frm.doc.amended_from) frm.set_value("period_end_date", frappe.datetime.now_datetime()); if (frm.doc.docstatus === 1) set_html_data(frm); }, diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index 07c8e44e77..493bd44802 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -195,18 +195,43 @@ frappe.ui.form.on('POS Invoice', { }, request_for_payment: function (frm) { + if (!frm.doc.contact_mobile) { + frappe.throw(__('Please enter mobile number first.')); + } + frm.dirty(); frm.save().then(() => { - frappe.dom.freeze(); - frappe.call({ - method: 'create_payment_request', - doc: frm.doc, - }) + frappe.dom.freeze(__('Waiting for payment...')); + frappe + .call({ + method: 'create_payment_request', + doc: frm.doc + }) .fail(() => { frappe.dom.unfreeze(); - frappe.msgprint('Payment request failed'); + frappe.msgprint(__('Payment request failed')); }) - .then(() => { - frappe.msgprint('Payment request sent successfully'); + .then(({ message }) => { + const payment_request_name = message.name; + setTimeout(() => { + frappe.db.get_value('Payment Request', payment_request_name, ['status', 'grand_total']).then(({ message }) => { + if (message.status != 'Paid') { + frappe.dom.unfreeze(); + frappe.msgprint({ + message: __('Payment Request took too long to respond. Please try requesting for payment again.'), + title: __('Request Timeout') + }); + } else if (frappe.dom.freeze_count != 0) { + frappe.dom.unfreeze(); + cur_frm.reload_doc(); + cur_pos.payment.events.submit_invoice(); + + frappe.show_alert({ + message: __("Payment of {0} received successfully.", [format_currency(message.grand_total, frm.doc.currency, 0)]), + indicator: 'green' + }); + } + }); + }, 60000); }); }); } diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 8d8babbe75..76e00923c4 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -179,10 +179,18 @@ class POSInvoice(SalesInvoice): if d.get("serial_no"): serial_nos = get_serial_nos(d.serial_no) for sr in serial_nos: - serial_no_exists = frappe.db.exists("POS Invoice Item", { - "parent": self.return_against, - "serial_no": ["like", d.get("serial_no")] - }) + serial_no_exists = frappe.db.sql(""" + SELECT name + FROM `tabPOS Invoice Item` + WHERE + parent = %s + and (serial_no = %s + or serial_no like %s + or serial_no like %s + or serial_no like %s + ) + """, (self.return_against, sr, sr+'\n%', '%\n'+sr, '%\n'+sr+'\n%')) + if not serial_no_exists: bold_return_against = frappe.bold(self.return_against) bold_serial_no = frappe.bold(sr) @@ -190,7 +198,7 @@ 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") @@ -292,7 +300,7 @@ class POSInvoice(SalesInvoice): if not self.get('payments') and not for_validate: update_multi_mode_option(self, profile) - + if self.is_return and not for_validate: add_return_modes(self, profile) @@ -317,13 +325,14 @@ class POSInvoice(SalesInvoice): ) customer_group_price_list = frappe.db.get_value("Customer Group", customer_group, 'default_price_list') selling_price_list = customer_price_list or customer_group_price_list or profile.get('selling_price_list') + if customer_currency != profile.get('currency'): + self.set('currency', customer_currency) + else: selling_price_list = profile.get('selling_price_list') if selling_price_list: self.set('selling_price_list', selling_price_list) - if customer_currency != profile.get('currency'): - self.set('currency', customer_currency) # set pos values in items for item in self.get("items"): @@ -383,22 +392,48 @@ class POSInvoice(SalesInvoice): if not self.contact_mobile: frappe.throw(_("Please enter the phone number first")) - payment_gateway = frappe.db.get_value("Payment Gateway Account", { - "payment_account": pay.account, - }) - record = { - "payment_gateway": payment_gateway, - "dt": "POS Invoice", - "dn": self.name, - "payment_request_type": "Inward", - "party_type": "Customer", - "party": self.customer, - "mode_of_payment": pay.mode_of_payment, - "recipient_id": self.contact_mobile, - "submit_doc": True - } + pay_req = self.get_existing_payment_request(pay) + if not pay_req: + pay_req = self.get_new_payment_request(pay) + pay_req.submit() + else: + pay_req.request_phone_payment() - return make_payment_request(**record) + return pay_req + + def get_new_payment_request(self, mop): + payment_gateway_account = frappe.db.get_value("Payment Gateway Account", { + "payment_account": mop.account, + }, ["name"]) + + args = { + "dt": "POS Invoice", + "dn": self.name, + "recipient_id": self.contact_mobile, + "mode_of_payment": mop.mode_of_payment, + "payment_gateway_account": payment_gateway_account, + "payment_request_type": "Inward", + "party_type": "Customer", + "party": self.customer, + "return_doc": True + } + return make_payment_request(**args) + + def get_existing_payment_request(self, pay): + payment_gateway_account = frappe.db.get_value("Payment Gateway Account", { + "payment_account": pay.account, + }, ["name"]) + + args = { + 'doctype': 'Payment Request', + 'reference_doctype': 'POS Invoice', + 'reference_name': self.name, + 'payment_gateway_account': payment_gateway_account, + 'email_to': self.contact_mobile + } + pr = frappe.db.exists(args) + if pr: + return frappe.get_doc('Payment Request', pr[0][0]) @frappe.whitelist() def get_stock_availability(item_code, warehouse): diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 57a23af8af..15875afe87 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -198,6 +198,65 @@ class TestPOSInvoice(unittest.TestCase): self.assertEqual(pos_return.get('payments')[0].amount, -500) self.assertEqual(pos_return.get('payments')[1].amount, -500) + def test_pos_return_for_serialized_item(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + + se = make_serialized_item(company='_Test Company', + target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC') + + serial_nos = get_serial_nos(se.get("items")[0].serial_no) + + pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC', + account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC', + expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC', + item=se.get("items")[0].item_code, rate=1000, do_not_save=1) + + pos.get("items")[0].serial_no = serial_nos[0] + pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1}) + + pos.insert() + pos.submit() + + pos_return = make_sales_return(pos.name) + + pos_return.insert() + pos_return.submit() + self.assertEqual(pos_return.get('items')[0].serial_no, serial_nos[0]) + + def test_partial_pos_returns(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + + se = make_serialized_item(company='_Test Company', + target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC') + + serial_nos = get_serial_nos(se.get("items")[0].serial_no) + + pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC', + account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC', + expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC', + item=se.get("items")[0].item_code, qty=2, rate=1000, do_not_save=1) + + pos.get("items")[0].serial_no = serial_nos[0] + "\n" + serial_nos[1] + pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1}) + + pos.insert() + pos.submit() + + pos_return1 = make_sales_return(pos.name) + + # partial return 1 + pos_return1.get('items')[0].qty = -1 + pos_return1.get('items')[0].serial_no = serial_nos[0] + pos_return1.insert() + pos_return1.submit() + + # partial return 2 + pos_return2 = make_sales_return(pos.name) + self.assertEqual(pos_return2.get('items')[0].qty, -1) + self.assertEqual(pos_return2.get('items')[0].serial_no, serial_nos[1]) + def test_pos_change_amount(self): pos = create_pos_invoice(company= "_Test Company", debit_to="Debtors - _TC", income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105, diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json index 2b6e7de118..8b71eb02fd 100644 --- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json +++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json @@ -87,6 +87,7 @@ "edit_references", "sales_order", "so_detail", + "pos_invoice_item", "column_break_74", "delivery_note", "dn_detail", @@ -790,11 +791,20 @@ "fieldtype": "Link", "label": "Project", "options": "Project" + }, + { + "fieldname": "pos_invoice_item", + "fieldtype": "Data", + "ignore_user_permissions": 1, + "label": "POS Invoice Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2020-07-22 13:40:34.418346", + "modified": "2021-01-04 17:34:49.924531", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice Item", diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index c88d67989b..40f77b4088 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -29,7 +29,7 @@ class POSInvoiceMergeLog(Document): for d in self.pos_invoices: status, docstatus, is_return, return_against = frappe.db.get_value( 'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against']) - + bold_pos_invoice = frappe.bold(d.pos_invoice) bold_status = frappe.bold(status) if docstatus != 1: @@ -58,7 +58,7 @@ class POSInvoiceMergeLog(Document): sales_invoice, credit_note = "", "" if sales: sales_invoice = self.process_merging_into_sales_invoice(sales) - + if returns: credit_note = self.process_merging_into_credit_note(returns) @@ -74,7 +74,7 @@ class POSInvoiceMergeLog(Document): def process_merging_into_sales_invoice(self, data): sales_invoice = self.get_new_sales_invoice() - + sales_invoice = self.merge_pos_invoice_into(sales_invoice, data) sales_invoice.is_consolidated = 1 @@ -98,19 +98,19 @@ class POSInvoiceMergeLog(Document): self.consolidated_credit_note = credit_note.name return credit_note.name - + def merge_pos_invoice_into(self, invoice, data): items, payments, taxes = [], [], [] loyalty_amount_sum, loyalty_points_sum = 0, 0 for doc in data: map_doc(doc, invoice, table_map={ "doctype": invoice.doctype }) - + if doc.redeem_loyalty_points: invoice.loyalty_redemption_account = doc.loyalty_redemption_account invoice.loyalty_redemption_cost_center = doc.loyalty_redemption_cost_center loyalty_points_sum += doc.loyalty_points loyalty_amount_sum += doc.loyalty_amount - + for item in doc.get('items'): found = False for i in items: @@ -118,12 +118,13 @@ class POSInvoiceMergeLog(Document): i.uom == item.uom and i.net_rate == item.net_rate): found = True i.qty = i.qty + item.qty + if not found: item.rate = item.net_rate item.price_list_rate = 0 si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"}) items.append(si_item) - + for tax in doc.get('taxes'): found = False for t in taxes: @@ -162,7 +163,7 @@ class POSInvoiceMergeLog(Document): invoice.ignore_pricing_rule = 1 return invoice - + def get_new_sales_invoice(self): sales_invoice = frappe.new_doc('Sales Invoice') sales_invoice.customer = self.customer @@ -194,7 +195,7 @@ def get_all_unconsolidated_invoices(): } pos_invoices = frappe.db.get_all('POS Invoice', filters=filters, fields=["name as pos_invoice", 'posting_date', 'grand_total', 'customer']) - + return pos_invoices def get_invoice_customer_map(pos_invoices): @@ -204,7 +205,7 @@ def get_invoice_customer_map(pos_invoices): customer = invoice.get('customer') pos_invoice_customer_map.setdefault(customer, []) pos_invoice_customer_map[customer].append(invoice) - + return pos_invoice_customer_map def consolidate_pos_invoices(pos_invoices=[], closing_entry={}): diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 1f7853dbf7..07e75acb41 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -40,6 +40,7 @@ "base_rate", "base_amount", "pricing_rules", + "stock_uom_rate", "is_free_item", "section_break_22", "net_rate", @@ -783,6 +784,14 @@ "print_hide": 1, "read_only": 1 }, + { + "depends_on": "eval: doc.uom != doc.stock_uom", + "fieldname": "stock_uom_rate", + "fieldtype": "Currency", + "label": "Rate of Stock UOM", + "options": "currency", + "read_only": 1 + }, { "fieldname": "sales_invoice_item", "fieldtype": "Data", @@ -795,7 +804,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-26 17:20:36.415791", + "modified": "2021-01-30 21:43:21.488258", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 7a98afff36..b403c7b237 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -45,6 +45,7 @@ "base_rate", "base_amount", "pricing_rules", + "stock_uom_rate", "is_free_item", "section_break_21", "net_rate", @@ -811,12 +812,20 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "depends_on": "eval: doc.uom != doc.stock_uom", + "fieldname": "stock_uom_rate", + "fieldtype": "Currency", + "label": "Rate of Stock UOM", + "options": "currency", + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-26 17:25:04.090630", + "modified": "2021-01-30 21:42:37.796771", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 287c79f13f..b42c0c61d9 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -44,9 +44,9 @@ def validate_accounting_period(gl_map): frappe.throw(_("You cannot create or cancel any accounting entries with in the closed Accounting Period {0}") .format(frappe.bold(accounting_periods[0].name)), ClosedAccountingPeriod) -def process_gl_map(gl_map, merge_entries=True): +def process_gl_map(gl_map, merge_entries=True, precision=None): if merge_entries: - gl_map = merge_similar_entries(gl_map) + gl_map = merge_similar_entries(gl_map, precision) for entry in gl_map: # toggle debit, credit if negative entry if flt(entry.debit) < 0: @@ -69,7 +69,7 @@ def process_gl_map(gl_map, merge_entries=True): return gl_map -def merge_similar_entries(gl_map): +def merge_similar_entries(gl_map, precision=None): merged_gl_map = [] accounting_dimensions = get_accounting_dimensions() for entry in gl_map: @@ -88,7 +88,9 @@ def merge_similar_entries(gl_map): company = gl_map[0].company if gl_map else erpnext.get_default_company() company_currency = erpnext.get_company_currency(company) - precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency) + + if not precision: + precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency) # filter zero debit and credit entries merged_gl_map = filter(lambda x: flt(x.debit, precision)!=0 or flt(x.credit, precision)!=0, merged_gl_map) @@ -132,8 +134,8 @@ def make_entry(args, adv_adj, update_outstanding, from_repost=False): gle.update(args) gle.flags.ignore_permissions = 1 gle.flags.from_repost = from_repost - gle.insert() - gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost) + gle.flags.adv_adj = adv_adj + gle.flags.update_outstanding = update_outstanding or 'Yes' gle.submit() if not from_repost: diff --git a/erpnext/accounts/page/bank_reconciliation/__init__.py b/erpnext/accounts/page/bank_reconciliation/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js deleted file mode 100644 index 6ae81d7402..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js +++ /dev/null @@ -1,583 +0,0 @@ -frappe.provide("erpnext.accounts"); - -frappe.pages['bank-reconciliation'].on_page_load = function(wrapper) { - new erpnext.accounts.bankReconciliation(wrapper); -} - -erpnext.accounts.bankReconciliation = class BankReconciliation { - constructor(wrapper) { - this.page = frappe.ui.make_app_page({ - parent: wrapper, - title: __("Bank Reconciliation"), - single_column: true - }); - this.parent = wrapper; - this.page = this.parent.page; - - this.check_plaid_status(); - this.make(); - } - - make() { - const me = this; - - me.$main_section = $(`
    `).appendTo(me.page.main); - const empty_state = __("Upload a bank statement, link or reconcile a bank account") - me.$main_section.append(`
    ${empty_state}
    `) - - me.page.add_field({ - fieldtype: 'Link', - label: __('Company'), - fieldname: 'company', - options: "Company", - onchange: function() { - if (this.value) { - me.company = this.value; - } else { - me.company = null; - me.bank_account = null; - } - } - }) - me.page.add_field({ - fieldtype: 'Link', - label: __('Bank Account'), - fieldname: 'bank_account', - options: "Bank Account", - get_query: function() { - if(!me.company) { - frappe.throw(__("Please select company first")); - return - } - - return { - filters: { - "company": me.company - } - } - }, - onchange: function() { - if (this.value) { - me.bank_account = this.value; - me.add_actions(); - } else { - me.bank_account = null; - me.page.hide_actions_menu(); - } - } - }) - } - - check_plaid_status() { - const me = this; - frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => { - if (r && r.enabled === "1") { - me.plaid_status = "active" - } else { - me.plaid_status = "inactive" - } - }) - } - - add_actions() { - const me = this; - - me.page.show_menu() - - me.page.add_menu_item(__("Upload a statement"), function() { - me.clear_page_content(); - new erpnext.accounts.bankTransactionUpload(me); - }, true) - - if (me.plaid_status==="active") { - me.page.add_menu_item(__("Synchronize this account"), function() { - me.clear_page_content(); - new erpnext.accounts.bankTransactionSync(me); - }, true) - } - - me.page.add_menu_item(__("Reconcile this account"), function() { - me.clear_page_content(); - me.make_reconciliation_tool(); - }, true) - } - - clear_page_content() { - const me = this; - $(me.page.body).find('.frappe-list').remove(); - me.$main_section.empty(); - } - - make_reconciliation_tool() { - const me = this; - frappe.model.with_doctype("Bank Transaction", () => { - erpnext.accounts.ReconciliationList = new erpnext.accounts.ReconciliationTool({ - parent: me.parent, - doctype: "Bank Transaction" - }); - }) - } -} - - -erpnext.accounts.bankTransactionUpload = class bankTransactionUpload { - constructor(parent) { - this.parent = parent; - this.data = []; - - const assets = [ - "/assets/frappe/css/frappe-datatable.css", - "/assets/frappe/js/lib/clusterize.min.js", - "/assets/frappe/js/lib/Sortable.min.js", - "/assets/frappe/js/lib/frappe-datatable.js" - ]; - - frappe.require(assets, () => { - this.make(); - }); - } - - make() { - const me = this; - new frappe.ui.FileUploader({ - method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement', - allow_multiple: 0, - on_success: function(attachment, r) { - if (!r.exc && r.message) { - me.data = r.message; - me.setup_transactions_dom(); - me.create_datatable(); - me.add_primary_action(); - } - } - }) - } - - setup_transactions_dom() { - const me = this; - me.parent.$main_section.append('
    '); - } - - create_datatable() { - try { - this.datatable = new DataTable('.transactions-table', { - columns: this.data.columns, - data: this.data.data - }) - } - catch(err) { - 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) - } - - } - - add_primary_action() { - const me = this; - me.parent.page.set_primary_action(__("Submit"), function() { - me.add_bank_entries() - }, null, __("Creating bank entries...")) - } - - add_bank_entries() { - const me = this; - frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.create_bank_entries', - {columns: this.datatable.datamanager.columns, data: this.datatable.datamanager.data, bank_account: me.parent.bank_account} - ).then((result) => { - let result_title = result.errors == 0 ? __("{0} bank transaction(s) created", [result.success]) : __("{0} bank transaction(s) created and {1} errors", [result.success, result.errors]) - let result_msg = ` -
    -
    ${result_title}
    -
    ` - me.parent.page.clear_primary_action(); - me.parent.$main_section.empty(); - me.parent.$main_section.append(result_msg); - if (result.errors == 0) { - frappe.show_alert({message:__("All bank transactions have been created"), indicator:'green'}); - } else { - frappe.show_alert({message:__("Please check the error log for details about the import errors"), indicator:'red'}); - } - }) - } -} - -erpnext.accounts.bankTransactionSync = class bankTransactionSync { - constructor(parent) { - this.parent = parent; - this.data = []; - - this.init_config() - } - - init_config() { - const me = this; - frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_plaid_configuration') - .then(result => { - me.plaid_env = result.plaid_env; - me.client_name = result.client_name; - me.link_token = result.link_token; - me.sync_transactions(); - }) - } - - sync_transactions() { - const me = this; - frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (r) => { - frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', { - bank: r.bank, - bank_account: me.parent.bank_account, - freeze: true - }) - .then((result) => { - let result_title = (result && result.length > 0) - ? __("{0} bank transaction(s) created", [result.length]) - : __("This bank account is already synchronized"); - - let result_msg = ` -
    -
    ${result_title}
    -
    ` - - this.parent.$main_section.append(result_msg) - frappe.show_alert({ message: __("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator: 'green' }); - }) - }) - } -} - - -erpnext.accounts.ReconciliationTool = class ReconciliationTool extends frappe.views.BaseList { - constructor(opts) { - super(opts); - this.show(); - } - - setup_defaults() { - super.setup_defaults(); - - this.page_title = __("Bank Reconciliation"); - this.doctype = 'Bank Transaction'; - this.fields = ['date', 'description', 'debit', 'credit', 'currency'] - - } - - setup_view() { - this.render_header(); - } - - setup_side_bar() { - // - } - - make_standard_filters() { - // - } - - freeze() { - this.$result.find('.list-count').html(`${__('Refreshing')}...`); - } - - get_args() { - const args = super.get_args(); - - return Object.assign({}, args, { - ...args.filters.push(["Bank Transaction", "docstatus", "=", 1], - ["Bank Transaction", "unallocated_amount", ">", 0]) - }); - - } - - update_data(r) { - let data = r.message || []; - - if (this.start === 0) { - this.data = data; - } else { - this.data = this.data.concat(data); - } - } - - render() { - const me = this; - this.$result.find('.list-row-container').remove(); - $('[data-fieldname="name"]').remove(); - me.data.map((value) => { - const row = $('
    ').data("data", value).appendTo(me.$result).get(0); - new erpnext.accounts.ReconciliationRow(row, value); - }) - } - - render_header() { - const me = this; - if ($(this.wrapper).find('.transaction-header').length === 0) { - me.$result.append(frappe.render_template("bank_transaction_header")); - } - } -} - -erpnext.accounts.ReconciliationRow = class ReconciliationRow { - constructor(row, data) { - this.data = data; - this.row = row; - this.make(); - this.bind_events(); - } - - make() { - $(this.row).append(frappe.render_template("bank_transaction_row", this.data)) - } - - bind_events() { - const me = this; - $(me.row).on('click', '.clickable-section', function() { - me.bank_entry = $(this).attr("data-name"); - me.show_dialog($(this).attr("data-name")); - }) - - $(me.row).on('click', '.new-reconciliation', function() { - me.bank_entry = $(this).attr("data-name"); - me.show_dialog($(this).attr("data-name")); - }) - - $(me.row).on('click', '.new-payment', function() { - me.bank_entry = $(this).attr("data-name"); - me.new_payment(); - }) - - $(me.row).on('click', '.new-invoice', function() { - me.bank_entry = $(this).attr("data-name"); - me.new_invoice(); - }) - - $(me.row).on('click', '.new-expense', function() { - me.bank_entry = $(this).attr("data-name"); - me.new_expense(); - }) - } - - new_payment() { - const me = this; - const paid_amount = me.data.credit > 0 ? me.data.credit : me.data.debit; - const payment_type = me.data.credit > 0 ? "Receive": "Pay"; - const party_type = me.data.credit > 0 ? "Customer": "Supplier"; - - frappe.new_doc("Payment Entry", {"payment_type": payment_type, "paid_amount": paid_amount, - "party_type": party_type, "paid_from": me.data.bank_account}) - } - - new_invoice() { - const me = this; - const invoice_type = me.data.credit > 0 ? "Sales Invoice" : "Purchase Invoice"; - - frappe.new_doc(invoice_type) - } - - new_expense() { - frappe.new_doc("Expense Claim") - } - - - show_dialog(data) { - const me = this; - - frappe.db.get_value("Bank Account", me.data.bank_account, "account", (r) => { - me.gl_account = r.account; - }) - - frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments', - { bank_transaction: data, freeze: true, freeze_message: __("Finding linked payments") } - ).then((result) => { - me.make_dialog(result) - }) - } - - make_dialog(data) { - const me = this; - me.selected_payment = null; - - const fields = [ - { - fieldtype: 'Section Break', - fieldname: 'section_break_1', - label: __('Automatic Reconciliation') - }, - { - fieldtype: 'HTML', - fieldname: 'payment_proposals' - }, - { - fieldtype: 'Section Break', - fieldname: 'section_break_2', - label: __('Search for a payment') - }, - { - fieldtype: 'Link', - fieldname: 'payment_doctype', - options: 'DocType', - label: 'Payment DocType', - get_query: () => { - return { - filters : { - "name": ["in", ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Expense Claim"]] - } - } - }, - }, - { - fieldtype: 'Column Break', - fieldname: 'column_break_1', - }, - { - fieldtype: 'Dynamic Link', - fieldname: 'payment_entry', - options: 'payment_doctype', - label: 'Payment Document', - get_query: () => { - let dt = this.dialog.fields_dict.payment_doctype.value; - if (dt === "Payment Entry") { - return { - query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.payment_entry_query", - filters : { - "bank_account": this.data.bank_account, - "company": this.data.company - } - } - } else if (dt === "Journal Entry") { - return { - query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.journal_entry_query", - filters : { - "bank_account": this.data.bank_account, - "company": this.data.company - } - } - } else if (dt === "Sales Invoice") { - return { - query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.sales_invoices_query" - } - } else if (dt === "Purchase Invoice") { - return { - filters : [ - ["Purchase Invoice", "ifnull(clearance_date, '')", "=", ""], - ["Purchase Invoice", "docstatus", "=", 1], - ["Purchase Invoice", "company", "=", this.data.company] - ] - } - } else if (dt === "Expense Claim") { - return { - filters : [ - ["Expense Claim", "ifnull(clearance_date, '')", "=", ""], - ["Expense Claim", "docstatus", "=", 1], - ["Expense Claim", "company", "=", this.data.company] - ] - } - } - }, - onchange: function() { - if (me.selected_payment !== this.value) { - me.selected_payment = this.value; - me.display_payment_details(this); - } - } - }, - { - fieldtype: 'Section Break', - fieldname: 'section_break_3' - }, - { - fieldtype: 'HTML', - fieldname: 'payment_details' - }, - ]; - - me.dialog = new frappe.ui.Dialog({ - title: __("Choose a corresponding payment"), - fields: fields, - size: "large" - }); - - const proposals_wrapper = me.dialog.fields_dict.payment_proposals.$wrapper; - if (data && data.length > 0) { - proposals_wrapper.append(frappe.render_template("linked_payment_header")); - data.map(value => { - proposals_wrapper.append(frappe.render_template("linked_payment_row", value)) - }) - } else { - const empty_data_msg = __("ERPNext could not find any matching payment entry") - proposals_wrapper.append(`
    ${empty_data_msg}
    `) - } - - $(me.dialog.body).on('click', '.reconciliation-btn', (e) => { - const payment_entry = $(e.target).attr('data-name'); - const payment_doctype = $(e.target).attr('data-doctype'); - frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.reconcile', - {bank_transaction: me.bank_entry, payment_doctype: payment_doctype, payment_name: payment_entry}) - .then((result) => { - setTimeout(function(){ - erpnext.accounts.ReconciliationList.refresh(); - }, 2000); - me.dialog.hide(); - }) - }) - - me.dialog.show(); - } - - display_payment_details(event) { - const me = this; - if (event.value) { - let dt = me.dialog.fields_dict.payment_doctype.value; - me.dialog.fields_dict['payment_details'].$wrapper.empty(); - frappe.db.get_doc(dt, event.value) - .then(doc => { - let displayed_docs = [] - let payment = [] - if (dt === "Payment Entry") { - payment.currency = doc.payment_type == "Receive" ? doc.paid_to_account_currency : doc.paid_from_account_currency; - payment.doctype = dt - payment.posting_date = doc.posting_date; - payment.party = doc.party; - payment.reference_no = doc.reference_no; - payment.reference_date = doc.reference_date; - payment.paid_amount = doc.paid_amount; - payment.name = doc.name; - displayed_docs.push(payment); - } else if (dt === "Journal Entry") { - doc.accounts.forEach(payment => { - if (payment.account === me.gl_account) { - payment.doctype = dt; - payment.posting_date = doc.posting_date; - payment.party = doc.pay_to_recd_from; - payment.reference_no = doc.cheque_no; - payment.reference_date = doc.cheque_date; - payment.currency = payment.account_currency; - payment.paid_amount = payment.credit > 0 ? payment.credit : payment.debit; - payment.name = doc.name; - displayed_docs.push(payment); - } - }) - } else if (dt === "Sales Invoice") { - doc.payments.forEach(payment => { - if (payment.clearance_date === null || payment.clearance_date === "") { - payment.doctype = dt; - payment.posting_date = doc.posting_date; - payment.party = doc.customer; - payment.reference_no = doc.remarks; - payment.currency = doc.currency; - payment.paid_amount = payment.amount; - payment.name = doc.name; - displayed_docs.push(payment); - } - }) - } - - const details_wrapper = me.dialog.fields_dict.payment_details.$wrapper; - details_wrapper.append(frappe.render_template("linked_payment_header")); - displayed_docs.forEach(payment => { - details_wrapper.append(frappe.render_template("linked_payment_row", payment)); - }) - }) - } - - } -} diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json deleted file mode 100644 index feea36860b..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "content": null, - "creation": "2018-11-24 12:03:14.646669", - "docstatus": 0, - "doctype": "Page", - "idx": 0, - "modified": "2018-11-24 12:03:14.646669", - "modified_by": "Administrator", - "module": "Accounts", - "name": "bank-reconciliation", - "owner": "Administrator", - "page_name": "bank-reconciliation", - "roles": [ - { - "role": "System Manager" - }, - { - "role": "Accounts Manager" - }, - { - "role": "Accounts User" - } - ], - "script": null, - "standard": "Yes", - "style": null, - "system_page": 0, - "title": "Bank Reconciliation" -} \ No newline at end of file diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py deleted file mode 100644 index 8abe20c00a..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py +++ /dev/null @@ -1,369 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -import difflib -from frappe.utils import flt -from six import iteritems -from erpnext import get_company_currency - -@frappe.whitelist() -def reconcile(bank_transaction, payment_doctype, payment_name): - transaction = frappe.get_doc("Bank Transaction", bank_transaction) - payment_entry = frappe.get_doc(payment_doctype, payment_name) - - account = frappe.db.get_value("Bank Account", transaction.bank_account, "account") - gl_entry = frappe.get_doc("GL Entry", dict(account=account, voucher_type=payment_doctype, voucher_no=payment_name)) - - if payment_doctype == "Payment Entry" and payment_entry.unallocated_amount > transaction.unallocated_amount: - frappe.throw(_("The unallocated amount of Payment Entry {0} is greater than the Bank Transaction's unallocated amount").format(payment_name)) - - if transaction.unallocated_amount == 0: - frappe.throw(_("This bank transaction is already fully reconciled")) - - if transaction.credit > 0 and gl_entry.credit > 0: - frappe.throw(_("The selected payment entry should be linked with a debtor bank transaction")) - - if transaction.debit > 0 and gl_entry.debit > 0: - frappe.throw(_("The selected payment entry should be linked with a creditor bank transaction")) - - add_payment_to_transaction(transaction, payment_entry, gl_entry) - - return 'reconciled' - -def add_payment_to_transaction(transaction, payment_entry, gl_entry): - gl_amount, transaction_amount = (gl_entry.credit, transaction.debit) if gl_entry.credit > 0 else (gl_entry.debit, transaction.credit) - allocated_amount = gl_amount if gl_amount <= transaction_amount else transaction_amount - transaction.append("payment_entries", { - "payment_document": payment_entry.doctype, - "payment_entry": payment_entry.name, - "allocated_amount": allocated_amount - }) - - transaction.save() - transaction.update_allocations() - -@frappe.whitelist() -def get_linked_payments(bank_transaction): - transaction = frappe.get_doc("Bank Transaction", bank_transaction) - bank_account = frappe.db.get_values("Bank Account", transaction.bank_account, ["account", "company"], as_dict=True) - - # Get all payment entries with a matching amount - amount_matching = check_matching_amount(bank_account[0].account, bank_account[0].company, transaction) - - # Get some data from payment entries linked to a corresponding bank transaction - description_matching = get_matching_descriptions_data(bank_account[0].company, transaction) - - if amount_matching: - return check_amount_vs_description(amount_matching, description_matching) - - elif description_matching: - description_matching = filter(lambda x: not x.get('clearance_date'), description_matching) - if not description_matching: - return [] - - return sorted(list(description_matching), key = lambda x: x["posting_date"], reverse=True) - - else: - return [] - -def check_matching_amount(bank_account, company, transaction): - payments = [] - amount = transaction.credit if transaction.credit > 0 else transaction.debit - - payment_type = "Receive" if transaction.credit > 0 else "Pay" - account_from_to = "paid_to" if transaction.credit > 0 else "paid_from" - currency_field = "paid_to_account_currency as currency" if transaction.credit > 0 else "paid_from_account_currency as currency" - - payment_entries = frappe.get_all("Payment Entry", fields=["'Payment Entry' as doctype", "name", "paid_amount", "payment_type", "reference_no", "reference_date", - "party", "party_type", "posting_date", "{0}".format(currency_field)], filters=[["paid_amount", "like", "{0}%".format(amount)], - ["docstatus", "=", "1"], ["payment_type", "=", [payment_type, "Internal Transfer"]], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]]) - - jea_side = "debit" if transaction.credit > 0 else "credit" - journal_entries = frappe.db.sql(f""" - SELECT - 'Journal Entry' as doctype, je.name, je.posting_date, je.cheque_no as reference_no, - jea.account_currency as currency, je.pay_to_recd_from as party, je.cheque_date as reference_date, - jea.{jea_side}_in_account_currency as paid_amount - FROM - `tabJournal Entry Account` as jea - JOIN - `tabJournal Entry` as je - ON - jea.parent = je.name - WHERE - (je.clearance_date is null or je.clearance_date='0000-00-00') - AND - jea.account = %(bank_account)s - AND - jea.{jea_side}_in_account_currency like %(txt)s - AND - je.docstatus = 1 - """, { - 'bank_account': bank_account, - 'txt': '%%%s%%' % amount - }, as_dict=True) - - if transaction.credit > 0: - sales_invoices = frappe.db.sql(""" - SELECT - 'Sales Invoice' as doctype, si.name, si.customer as party, - si.posting_date, sip.amount as paid_amount - FROM - `tabSales Invoice Payment` as sip - JOIN - `tabSales Invoice` as si - ON - sip.parent = si.name - WHERE - (sip.clearance_date is null or sip.clearance_date='0000-00-00') - AND - sip.account = %s - AND - sip.amount like %s - AND - si.docstatus = 1 - """, (bank_account, amount), as_dict=True) - else: - sales_invoices = [] - - if transaction.debit > 0: - purchase_invoices = frappe.get_all("Purchase Invoice", - fields = ["'Purchase Invoice' as doctype", "name", "paid_amount", "supplier as party", "posting_date", "currency"], - filters=[ - ["paid_amount", "like", "{0}%".format(amount)], - ["docstatus", "=", "1"], - ["is_paid", "=", "1"], - ["ifnull(clearance_date, '')", "=", ""], - ["cash_bank_account", "=", "{0}".format(bank_account)] - ] - ) - - mode_of_payments = [x["parent"] for x in frappe.db.get_list("Mode of Payment Account", - filters={"default_account": bank_account}, fields=["parent"])] - - company_currency = get_company_currency(company) - - expense_claims = frappe.get_all("Expense Claim", - fields=["'Expense Claim' as doctype", "name", "total_sanctioned_amount as paid_amount", - "employee as party", "posting_date", "'{0}' as currency".format(company_currency)], - filters=[ - ["total_sanctioned_amount", "like", "{0}%".format(amount)], - ["docstatus", "=", "1"], - ["is_paid", "=", "1"], - ["ifnull(clearance_date, '')", "=", ""], - ["mode_of_payment", "in", "{0}".format(tuple(mode_of_payments))] - ] - ) - else: - purchase_invoices = expense_claims = [] - - for data in [payment_entries, journal_entries, sales_invoices, purchase_invoices, expense_claims]: - if data: - payments.extend(data) - - return payments - -def get_matching_descriptions_data(company, transaction): - if not transaction.description : - return [] - - bank_transactions = frappe.db.sql(""" - SELECT - bt.name, bt.description, bt.date, btp.payment_document, btp.payment_entry - FROM - `tabBank Transaction` as bt - LEFT JOIN - `tabBank Transaction Payments` as btp - ON - bt.name = btp.parent - WHERE - bt.allocated_amount > 0 - AND - bt.docstatus = 1 - """, as_dict=True) - - selection = [] - for bank_transaction in bank_transactions: - if bank_transaction.description: - seq=difflib.SequenceMatcher(lambda x: x == " ", transaction.description, bank_transaction.description) - - if seq.ratio() > 0.6: - bank_transaction["ratio"] = seq.ratio() - selection.append(bank_transaction) - - document_types = set([x["payment_document"] for x in selection]) - - links = {} - for document_type in document_types: - links[document_type] = [x["payment_entry"] for x in selection if x["payment_document"]==document_type] - - - data = [] - company_currency = get_company_currency(company) - for key, value in iteritems(links): - if key == "Payment Entry": - data.extend(frappe.get_all("Payment Entry", filters=[["name", "in", value]], - fields=["'Payment Entry' as doctype", "posting_date", "party", "reference_no", - "reference_date", "paid_amount", "paid_to_account_currency as currency", "clearance_date"])) - if key == "Journal Entry": - journal_entries = frappe.get_all("Journal Entry", filters=[["name", "in", value]], - fields=["name", "'Journal Entry' as doctype", "posting_date", - "pay_to_recd_from as party", "cheque_no as reference_no", "cheque_date as reference_date", - "total_credit as paid_amount", "clearance_date"]) - for journal_entry in journal_entries: - journal_entry_accounts = frappe.get_all("Journal Entry Account", filters={"parenttype": journal_entry["doctype"], "parent": journal_entry["name"]}, fields=["account_currency"]) - journal_entry["currency"] = journal_entry_accounts[0]["account_currency"] if journal_entry_accounts else company_currency - data.extend(journal_entries) - if key == "Sales Invoice": - data.extend(frappe.get_all("Sales Invoice", filters=[["name", "in", value]], fields=["'Sales Invoice' as doctype", "posting_date", "customer_name as party", "paid_amount", "currency"])) - if key == "Purchase Invoice": - data.extend(frappe.get_all("Purchase Invoice", filters=[["name", "in", value]], fields=["'Purchase Invoice' as doctype", "posting_date", "supplier_name as party", "paid_amount", "currency"])) - if key == "Expense Claim": - expense_claims = frappe.get_all("Expense Claim", filters=[["name", "in", value]], fields=["'Expense Claim' as doctype", "posting_date", "employee_name as party", "total_amount_reimbursed as paid_amount"]) - data.extend([dict(x,**{"currency": company_currency}) for x in expense_claims]) - - return data - -def check_amount_vs_description(amount_matching, description_matching): - result = [] - - if description_matching: - for am_match in amount_matching: - for des_match in description_matching: - if des_match.get("clearance_date"): - continue - - if am_match["party"] == des_match["party"]: - if am_match not in result: - result.append(am_match) - continue - - if "reference_no" in am_match and "reference_no" in des_match: - # Sequence Matcher does not handle None as input - am_reference = am_match["reference_no"] or "" - des_reference = des_match["reference_no"] or "" - - if difflib.SequenceMatcher(lambda x: x == " ", am_reference, des_reference).ratio() > 70: - if am_match not in result: - result.append(am_match) - if result: - return sorted(result, key = lambda x: x["posting_date"], reverse=True) - else: - return sorted(amount_matching, key = lambda x: x["posting_date"], reverse=True) - - else: - return sorted(amount_matching, key = lambda x: x["posting_date"], reverse=True) - -def get_matching_transactions_payments(description_matching): - payments = [x["payment_entry"] for x in description_matching] - - payment_by_ratio = {x["payment_entry"]: x["ratio"] for x in description_matching} - - if payments: - reference_payment_list = frappe.get_all("Payment Entry", fields=["name", "paid_amount", "payment_type", "reference_no", "reference_date", - "party", "party_type", "posting_date", "paid_to_account_currency"], filters=[["name", "in", payments]]) - - return sorted(reference_payment_list, key=lambda x: payment_by_ratio[x["name"]]) - - else: - return [] - -@frappe.whitelist() -@frappe.validate_and_sanitize_search_inputs -def payment_entry_query(doctype, txt, searchfield, start, page_len, filters): - account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account") - if not account: - return - - return frappe.db.sql(""" - SELECT - name, party, paid_amount, received_amount, reference_no - FROM - `tabPayment Entry` - WHERE - (clearance_date is null or clearance_date='0000-00-00') - AND (paid_from = %(account)s or paid_to = %(account)s) - AND (name like %(txt)s or party like %(txt)s) - AND docstatus = 1 - ORDER BY - if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), name - LIMIT - %(start)s, %(page_len)s""", - { - 'txt': "%%%s%%" % txt, - '_txt': txt.replace("%", ""), - 'start': start, - 'page_len': page_len, - 'account': account - } - ) - -@frappe.whitelist() -@frappe.validate_and_sanitize_search_inputs -def journal_entry_query(doctype, txt, searchfield, start, page_len, filters): - account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account") - - return frappe.db.sql(""" - SELECT - jea.parent, je.pay_to_recd_from, - if(jea.debit_in_account_currency > 0, jea.debit_in_account_currency, jea.credit_in_account_currency) - FROM - `tabJournal Entry Account` as jea - LEFT JOIN - `tabJournal Entry` as je - ON - jea.parent = je.name - WHERE - (je.clearance_date is null or je.clearance_date='0000-00-00') - AND - jea.account = %(account)s - AND - (jea.parent like %(txt)s or je.pay_to_recd_from like %(txt)s) - AND - je.docstatus = 1 - ORDER BY - if(locate(%(_txt)s, jea.parent), locate(%(_txt)s, jea.parent), 99999), - jea.parent - LIMIT - %(start)s, %(page_len)s""", - { - 'txt': "%%%s%%" % txt, - '_txt': txt.replace("%", ""), - 'start': start, - 'page_len': page_len, - 'account': account - } - ) - -@frappe.whitelist() -@frappe.validate_and_sanitize_search_inputs -def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters): - return frappe.db.sql(""" - SELECT - sip.parent, si.customer, sip.amount, sip.mode_of_payment - FROM - `tabSales Invoice Payment` as sip - LEFT JOIN - `tabSales Invoice` as si - ON - sip.parent = si.name - WHERE - (sip.clearance_date is null or sip.clearance_date='0000-00-00') - AND - (sip.parent like %(txt)s or si.customer like %(txt)s) - ORDER BY - if(locate(%(_txt)s, sip.parent), locate(%(_txt)s, sip.parent), 99999), - sip.parent - LIMIT - %(start)s, %(page_len)s""", - { - 'txt': "%%%s%%" % txt, - '_txt': txt.replace("%", ""), - 'start': start, - 'page_len': page_len - } - ) diff --git a/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html b/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html deleted file mode 100644 index 94f183b793..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html +++ /dev/null @@ -1,21 +0,0 @@ -
    -
    - -
    - {{ __("Description") }} -
    - - - -
    -
    -
    -
    diff --git a/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html b/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html deleted file mode 100644 index 742b84c63f..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html +++ /dev/null @@ -1,36 +0,0 @@ -
    -
    -
    - -
    - {{ description }} -
    - - - -
    - -
    -
    diff --git a/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html b/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html deleted file mode 100644 index 4542c36e0d..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html +++ /dev/null @@ -1,21 +0,0 @@ -
    -
    -
    - {{ __("Payment Name") }} -
    -
    - {{ __("Reference Date") }} -
    - - -
    - {{ __("Reference Number") }} -
    -
    -
    -
    -
    diff --git a/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html b/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html deleted file mode 100644 index bdbc9fce03..0000000000 --- a/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html +++ /dev/null @@ -1,36 +0,0 @@ -
    -
    -
    - {{ name }} -
    -
    - {% if (typeof reference_date !== "undefined") %} - {%= frappe.datetime.str_to_user(reference_date) %} - {% else %} - {% if (typeof posting_date !== "undefined") %} - {%= frappe.datetime.str_to_user(posting_date) %} - {% endif %} - {% endif %} -
    - - -
    - {% if (typeof reference_no !== "undefined") %} - {{ reference_no }} - {% else %} - {{ "" }} - {% endif %} -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py index 0861b20f14..79b0a6f30e 100644 --- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py +++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py @@ -15,15 +15,51 @@ def execute(filters=None): return columns, data def get_columns(): - return [ - _("Payment Document") + "::130", - _("Payment Entry") + ":Dynamic Link/"+_("Payment Document")+":110", - _("Posting Date") + ":Date:100", - _("Cheque/Reference No") + "::120", - _("Clearance Date") + ":Date:100", - _("Against Account") + ":Link/Account:170", - _("Amount") + ":Currency:120" - ] + columns = [{ + "label": _("Payment Document Type"), + "fieldname": "payment_document_type", + "fieldtype": "Link", + "options": "Doctype", + "width": 130 + }, + { + "label": _("Payment Entry"), + "fieldname": "payment_entry", + "fieldtype": "Dynamic Link", + "options": "payment_document_type", + "width": 140 + }, + { + "label": _("Posting Date"), + "fieldname": "posting_date", + "fieldtype": "Date", + "width": 100 + }, + { + "label": _("Cheque/Reference No"), + "fieldname": "cheque_no", + "width": 120 + }, + { + "label": _("Clearance Date"), + "fieldname": "clearance_date", + "fieldtype": "Date", + "width": 100 + }, + { + "label": _("Against Account"), + "fieldname": "against", + "fieldtype": "Link", + "options": "Account", + "width": 170 + }, + { + "label": _("Amount"), + "fieldname": "amount", + "width": 120 + }] + + return columns def get_conditions(filters): conditions = "" diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index d0116890b6..76f3c50578 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -222,7 +222,7 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i set_gl_entries_by_account(start_date, end_date, root.lft, root.rgt, filters, - gl_entries_by_account, accounts_by_name, ignore_closing_entries=False) + gl_entries_by_account, accounts_by_name, accounts, ignore_closing_entries=False) calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters) accumulate_values_into_parents(accounts, accounts_by_name, companies) @@ -339,7 +339,7 @@ def prepare_data(accounts, start_date, end_date, balance_must_be, companies, com return data def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account, - accounts_by_name, ignore_closing_entries=False): + accounts_by_name, accounts, ignore_closing_entries=False): """Returns a dict like { "account": [gl entries], ... }""" company_lft, company_rgt = frappe.get_cached_value('Company', @@ -382,15 +382,31 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g for entry in gl_entries: key = entry.account_number or entry.account_name - validate_entries(key, entry, accounts_by_name) + validate_entries(key, entry, accounts_by_name, accounts) gl_entries_by_account.setdefault(key, []).append(entry) return gl_entries_by_account -def validate_entries(key, entry, accounts_by_name): +def get_account_details(account): + return frappe.get_cached_value('Account', account, ['name', 'report_type', 'root_type', 'company', + 'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1) + +def validate_entries(key, entry, accounts_by_name, accounts): if key not in accounts_by_name: - field = "Account number" if entry.account_number else "Account name" - frappe.throw(_("{0} {1} is not present in the parent company").format(field, key)) + args = get_account_details(entry.account) + + if args.parent_account: + parent_args = get_account_details(args.parent_account) + + args.update({ + 'lft': parent_args.lft + 1, + 'rgt': parent_args.rgt - 1, + 'root_type': parent_args.root_type, + 'report_type': parent_args.report_type + }) + + accounts_by_name.setdefault(key, args) + accounts.append(args) def get_additional_conditions(from_date, ignore_closing_entries, filters): additional_conditions = [] diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 67c7fd2d22..5eb2aab393 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -82,7 +82,7 @@ def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verb error_msg = _("""{0} {1} is not in any active Fiscal Year""").format(label, formatdate(transaction_date)) if company: error_msg = _("""{0} for {1}""").format(error_msg, frappe.bold(company)) - + if verbose==1: frappe.msgprint(error_msg) raise FiscalYearError(error_msg) @@ -888,19 +888,23 @@ def get_coa(doctype, parent, is_root, chart=None): def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, warehouse_account=None, company=None): + stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items, company) + repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company, warehouse_account) + + +def repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company=None, warehouse_account=None): def _delete_gl_entries(voucher_type, voucher_no): frappe.db.sql("""delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no)) + if not warehouse_account: warehouse_account = get_warehouse_account_map(company) - future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items) - gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date) - - for voucher_type, voucher_no in future_stock_vouchers: + gle = get_voucherwise_gl_entries(stock_vouchers, posting_date) + for voucher_type, voucher_no in stock_vouchers: existing_gle = gle.get((voucher_type, voucher_no), []) - voucher_obj = frappe.get_doc(voucher_type, voucher_no) + voucher_obj = frappe.get_cached_doc(voucher_type, voucher_no) expected_gle = voucher_obj.get_gl_entries(warehouse_account) if expected_gle: if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle): @@ -909,7 +913,7 @@ def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for else: _delete_gl_entries(voucher_type, voucher_no) -def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None): +def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None, company=None): future_stock_vouchers = [] values = [] @@ -922,6 +926,10 @@ def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, f condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses))) values += for_warehouses + if company: + condition += " and company = %s" + values.append(company) + for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no from `tabStock Ledger Entry` sle where @@ -982,7 +990,7 @@ def check_if_stock_and_account_balance_synced(posting_date, company, voucher_typ error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses as on {3}.").format( stock_bal, account_bal, frappe.bold(account), posting_date) error_resolution = _("Please create an adjustment Journal Entry for amount {0} on {1}")\ - .format(frappe.bold(diff), frappe.bold(posting_date)) + .format(frappe.bold(diff), frappe.bold(posting_date)) frappe.msgprint( msg="""{0}

    {1}

    """.format(error_reason, error_resolution), 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 c691e9f9f8..75b2954ddd 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -40,6 +40,7 @@ "base_rate", "base_amount", "pricing_rules", + "stock_uom_rate", "is_free_item", "section_break_29", "net_rate", @@ -726,13 +727,21 @@ "fieldname": "more_info_section_break", "fieldtype": "Section Break", "label": "More Information" + }, + { + "depends_on": "eval: doc.uom != doc.stock_uom", + "fieldname": "stock_uom_rate", + "fieldtype": "Currency", + "label": "Rate of Stock UOM", + "options": "currency", + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-12-07 11:59:47.670951", + "modified": "2021-01-30 21:44:41.816974", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index a51498e935..7cf22f87e4 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -127,6 +127,10 @@ class RequestforQuotation(BuyingController): 'link_doctype': 'Supplier', 'link_name': rfq_supplier.supplier }) + contact.append('email_ids', { + 'email_id': user.name, + 'is_primary': 1 + }) if not contact.email_id and not contact.user: contact.email_id = user.name diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 40362b1d40..4cc5753cbd 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -26,7 +26,6 @@ "supplier_group", "supplier_type", "pan", - "language", "allow_purchase_invoice_creation_without_purchase_order", "allow_purchase_invoice_creation_without_purchase_receipt", "disabled", @@ -57,6 +56,7 @@ "website", "supplier_details", "column_break_30", + "language", "is_frozen" ], "fields": [ @@ -384,7 +384,7 @@ "idx": 370, "image_field": "image", "links": [], - "modified": "2020-06-17 23:18:20", + "modified": "2021-01-06 19:51:40.939087", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 991eef1d21..12a81c7887 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1309,45 +1309,28 @@ def add_taxes_from_tax_template(child_item, parent_doc): }) tax_row.db_insert() -def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item): +def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, trans_item): """ - Returns a Sales Order Item child item containing the default values + Returns a Sales/Purchase Order Item child item containing the default values """ p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) - child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname) + child_item = frappe.new_doc(child_doctype, p_doc, child_docname) item = frappe.get_doc("Item", trans_item.get('item_code')) - child_item.item_code = item.item_code - child_item.item_name = item.item_name - child_item.description = item.description - child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date + for field in ("item_code", "item_name", "description", "item_group"): + child_item.update({field: item.get(field)}) + date_fieldname = "delivery_date" if child_doctype == "Sales Order Item" else "schedule_date" + child_item.update({date_fieldname: trans_item.get(date_fieldname) or p_doc.get(date_fieldname)}) child_item.uom = trans_item.get("uom") or item.stock_uom conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor - set_child_tax_template_and_map(item, child_item, p_doc) - add_taxes_from_tax_template(child_item, p_doc) - child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) - if not child_item.warehouse: - frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") - .format(frappe.bold("default warehouse"), frappe.bold(item.item_code))) - return child_item - - -def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item): - """ - Returns a Purchase Order Item child item containing the default values - """ - p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) - child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname) - item = frappe.get_doc("Item", trans_item.get('item_code')) - child_item.item_code = item.item_code - child_item.item_name = item.item_name - child_item.description = item.description - child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date - child_item.uom = trans_item.get("uom") or item.stock_uom - conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) - child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor - child_item.base_rate = 1 # Initiallize value will update in parent validation - child_item.base_amount = 1 # Initiallize value will update in parent validation + if child_doctype == "Purchase Order Item": + child_item.base_rate = 1 # Initiallize value will update in parent validation + child_item.base_amount = 1 # Initiallize value will update in parent validation + if child_doctype == "Sales Order Item": + child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) + if not child_item.warehouse: + frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") + .format(frappe.bold("default warehouse"), frappe.bold(item.item_code))) set_child_tax_template_and_map(item, child_item, p_doc) add_taxes_from_tax_template(child_item, p_doc) return child_item @@ -1411,8 +1394,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil ) def get_new_child_item(item_row): - new_child_function = set_sales_order_defaults if parent_doctype == "Sales Order" else set_purchase_order_defaults - return new_child_function(parent_doctype, parent_doctype_name, child_docname, item_row) + child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item" + return set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, item_row) def validate_quantity(child_item, d): if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 0e1829a767..de61b35316 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -204,8 +204,6 @@ def get_already_returned_items(doc): return items def get_returned_qty_map_for_row(row_name, doctype): - if doctype == "POS Invoice": return {} - child_doctype = doctype + " Item" reference_field = "dn_detail" if doctype == "Delivery Note" else frappe.scrub(child_doctype) @@ -354,7 +352,12 @@ def make_return_doc(doctype, source_name, target_doc=None): target_doc.so_detail = source_doc.so_detail target_doc.dn_detail = source_doc.dn_detail target_doc.expense_account = source_doc.expense_account - target_doc.sales_invoice_item = source_doc.name + + if doctype == "Sales Invoice": + target_doc.sales_invoice_item = source_doc.name + else: + target_doc.pos_invoice_item = source_doc.name + target_doc.price_list_rate = 0 if default_warehouse_for_sales_return: target_doc.warehouse = default_warehouse_for_sales_return diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 6abfe04db9..c61b67b0a4 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -446,9 +446,13 @@ class SellingController(StockController): check_list, chk_dupl_itm = [], [] if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")): return + if self.doctype == "Sales Invoice" and self.is_consolidated: + return + if self.doctype == "POS Invoice": + return for d in self.get('items'): - if self.doctype in ["POS Invoice","Sales Invoice"]: + if self.doctype == "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": diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 2ae9dc7102..e0031c9c69 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -24,6 +24,7 @@ class StockController(AccountsController): self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() + self.set_rate_of_stock_uom() self.validate_internal_transfer() self.validate_putaway_capacity() @@ -73,7 +74,7 @@ class StockController(AccountsController): gl_list = [] warehouse_with_no_account = [] - precision = frappe.get_precision("GL Entry", "debit_in_account_currency") + precision = self.get_debit_field_precision() for item_row in voucher_details: sle_list = sle_map.get(item_row.name) @@ -130,7 +131,13 @@ class StockController(AccountsController): if frappe.db.get_value("Warehouse", wh, "company"): frappe.throw(_("Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.").format(wh, self.company)) - return process_gl_map(gl_list) + return process_gl_map(gl_list, precision=precision) + + def get_debit_field_precision(self): + if not frappe.flags.debit_field_precision: + frappe.flags.debit_field_precision = frappe.get_precision("GL Entry", "debit_in_account_currency") + + return frappe.flags.debit_field_precision def update_stock_ledger_entries(self, sle): sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, @@ -243,7 +250,7 @@ class StockController(AccountsController): .format(item.idx, frappe.bold(item.item_code), msg), title=_("Expense Account Missing")) else: - is_expense_account = frappe.db.get_value("Account", + is_expense_account = frappe.get_cached_value("Account", item.get("expense_account"), "report_type")=="Profit and Loss" if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Stock Reconciliation", "Stock Entry") and not is_expense_account: frappe.throw(_("Expense / Difference account ({0}) must be a 'Profit or Loss' account") @@ -396,6 +403,11 @@ class StockController(AccountsController): if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): d.allow_zero_valuation_rate = 1 + def set_rate_of_stock_uom(self): + if self.doctype in ["Purchase Receipt", "Purchase Invoice", "Purchase Order", "Sales Invoice", "Sales Order", "Delivery Note", "Quotation"]: + for d in self.get("items"): + d.stock_uom_rate = d.rate / d.conversion_factor + def validate_internal_transfer(self): if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \ and self.is_internal_transfer(): @@ -482,7 +494,6 @@ class StockController(AccountsController): "voucher_no": self.name, "company": self.company }) - if check_if_future_sle_exists(args): create_repost_item_valuation_entry(args) elif not is_reposting_pending(): diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index cfa499191c..10271cbcc9 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -15,6 +15,8 @@ from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_ra class calculate_taxes_and_totals(object): def __init__(self, doc): self.doc = doc + frappe.flags.round_off_applicable_accounts = [] + get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts) self.calculate() def calculate(self): @@ -332,10 +334,18 @@ class calculate_taxes_and_totals(object): elif tax.charge_type == "On Item Quantity": current_tax_amount = tax_rate * item.qty + current_tax_amount = self.get_final_current_tax_amount(tax, current_tax_amount) self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount) return current_tax_amount + def get_final_current_tax_amount(self, tax, current_tax_amount): + # Some countries need individual tax components to be rounded + # Handeled via regional doctypess + if tax.account_head in frappe.flags.round_off_applicable_accounts: + current_tax_amount = round(current_tax_amount, 0) + return current_tax_amount + def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount): # store tax breakup for each item key = item.item_code or item.item_name @@ -693,6 +703,15 @@ def get_itemised_tax_breakup_html(doc): ) ) +@frappe.whitelist() +def get_round_off_applicable_accounts(company, account_list): + account_list = get_regional_round_off_accounts(company, account_list) + + return account_list + +@erpnext.allow_regional +def get_regional_round_off_accounts(company, account_list): + pass @erpnext.allow_regional def update_itemised_tax_data(doc): diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index 2df1793fdb..1b33fd73ac 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -49,6 +49,7 @@ "phone", "mobile_no", "fax", + "website", "more_info", "type", "market_segment", @@ -56,8 +57,8 @@ "request_type", "column_break3", "company", - "website", "territory", + "language", "unsubscribed", "blog_subscriber", "title" @@ -447,13 +448,19 @@ "fieldtype": "Select", "label": "Address Type", "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nCurrent\nPermanent\nOther" + }, + { + "fieldname": "language", + "fieldtype": "Link", + "label": "Print Language", + "options": "Language" } ], "icon": "fa fa-user", "idx": 5, "image_field": "image", "links": [], - "modified": "2020-10-13 15:24:00.094811", + "modified": "2021-01-06 19:39:58.748978", "modified_by": "Administrator", "module": "CRM", "name": "Lead", diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 08958b7dd6..ac374a95f4 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -24,6 +24,12 @@ frappe.ui.form.on("Opportunity", { frm.trigger('set_contact_link'); } }, + contact_date: function(frm) { + if(frm.doc.contact_date < frappe.datetime.now_datetime()){ + frm.set_value("contact_date", ""); + frappe.throw(__("Next follow up date should be greater than now.")) + } + }, onload_post_render: function(frm) { frm.get_field("items").grid.set_multiple_add("item_code", "qty"); diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index eee13f7e79..2e09a76c0f 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -54,6 +54,7 @@ "campaign", "column_break1", "transaction_date", + "language", "amended_from", "lost_reasons" ], @@ -419,12 +420,18 @@ "fieldtype": "Duration", "label": "First Response Time", "read_only": 1 + }, + { + "fieldname": "language", + "fieldtype": "Link", + "label": "Print Language", + "options": "Language" } ], "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2020-08-12 17:34:35.066961", + "modified": "2021-01-06 19:42:46.190051", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", diff --git a/erpnext/crm/doctype/utils.py b/erpnext/crm/doctype/utils.py index 4ccd9bd73b..f244daffea 100644 --- a/erpnext/crm/doctype/utils.py +++ b/erpnext/crm/doctype/utils.py @@ -78,7 +78,9 @@ def get_scheduled_employees_for_popup(communication_medium): def strip_number(number): if not number: return - # strip 0 from the start of the number for proper number comparisions + # strip + and 0 from the start of the number for proper number comparisions + # eg. +7888383332 should match with 7888383332 # eg. 07888383332 should match with 7888383332 + number = number.lstrip('+') number = number.lstrip('0') return number diff --git a/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py b/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py index b538a58189..3a9d57d607 100644 --- a/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py +++ b/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py @@ -19,15 +19,50 @@ def set_defaut_value_for_filters(filters): if not filters.get('lead_age'): filters["lead_age"] = 60 def get_columns(): - return [ - _("Lead") + ":Link/Lead:100", - _("Name") + "::100", - _("Organization") + "::100", - _("Reference Document") + "::150", - _("Reference Name") + ":Dynamic Link/"+_("Reference Document")+":120", - _("Last Communication") + ":Data:200", - _("Last Communication Date") + ":Date:180" - ] + columns = [{ + "label": _("Lead"), + "fieldname": "lead", + "fieldtype": "Link", + "options": "Lead", + "width": 130 + }, + { + "label": _("Name"), + "fieldname": "name", + "width": 120 + }, + { + "label": _("Organization"), + "fieldname": "organization", + "width": 120 + }, + { + "label": _("Reference Document Type"), + "fieldname": "reference_document_type", + "fieldtype": "Link", + "options": "Doctype", + "width": 100 + }, + { + "label": _("Reference Name"), + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "options": "reference_document_type", + "width": 140 + }, + { + "label": _("Last Communication"), + "fieldname": "last_communication", + "fieldtype": "Data", + "width": 200 + }, + { + "label": _("Last Communication Date"), + "fieldname": "last_communication_date", + "fieldtype": "Date", + "width": 100 + }] + return columns def get_data(filters): lead_details = [] diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py index d33b0a7089..554c6b0eb0 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py @@ -5,7 +5,7 @@ import datetime class MpesaConnector(): def __init__(self, env="sandbox", app_key=None, app_secret=None, sandbox_url="https://sandbox.safaricom.co.ke", - live_url="https://safaricom.co.ke"): + live_url="https://api.safaricom.co.ke"): """Setup configuration for Mpesa connector and generate new access token.""" self.env = env self.app_key = app_key @@ -102,14 +102,14 @@ class MpesaConnector(): "BusinessShortCode": business_shortcode, "Password": encoded.decode("utf-8"), "Timestamp": time, - "TransactionType": "CustomerPayBillOnline", "Amount": amount, "PartyA": int(phone_number), - "PartyB": business_shortcode, + "PartyB": reference_code, "PhoneNumber": int(phone_number), "CallBackURL": callback_url, "AccountReference": reference_code, - "TransactionDesc": description + "TransactionDesc": description, + "TransactionType": "CustomerPayBillOnline" if self.env == "sandbox" else "CustomerBuyGoodsOnline" } headers = {'Authorization': 'Bearer {0}'.format(self.authentication_token), 'Content-Type': "application/json"} diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json index fc7b310c08..407f82616f 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json @@ -11,8 +11,10 @@ "consumer_secret", "initiator_name", "till_number", + "transaction_limit", "sandbox", "column_break_4", + "business_shortcode", "online_passkey", "security_credential", "get_account_balance", @@ -84,10 +86,24 @@ "fieldname": "get_account_balance", "fieldtype": "Button", "label": "Get Account Balance" + }, + { + "depends_on": "eval:(doc.sandbox==0)", + "fieldname": "business_shortcode", + "fieldtype": "Data", + "label": "Business Shortcode", + "mandatory_depends_on": "eval:(doc.sandbox==0)" + }, + { + "default": "150000", + "fieldname": "transaction_limit", + "fieldtype": "Float", + "label": "Transaction Limit", + "non_negative": 1 } ], "links": [], - "modified": "2020-09-25 20:21:38.215494", + "modified": "2021-01-29 12:02:16.106942", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Mpesa Settings", diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py index 1cad84dcde..b5718026c1 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py @@ -33,13 +33,34 @@ class MpesaSettings(Document): create_mode_of_payment('Mpesa-' + self.payment_gateway_name, payment_type="Phone") def request_for_payment(self, **kwargs): - if frappe.flags.in_test: - from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import get_payment_request_response_payload - response = frappe._dict(get_payment_request_response_payload()) - else: - response = frappe._dict(generate_stk_push(**kwargs)) + args = frappe._dict(kwargs) + request_amounts = self.split_request_amount_according_to_transaction_limit(args) - self.handle_api_response("CheckoutRequestID", kwargs, response) + for i, amount in enumerate(request_amounts): + args.request_amount = amount + if frappe.flags.in_test: + from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import get_payment_request_response_payload + response = frappe._dict(get_payment_request_response_payload(amount)) + else: + response = frappe._dict(generate_stk_push(**args)) + + self.handle_api_response("CheckoutRequestID", args, response) + + def split_request_amount_according_to_transaction_limit(self, args): + request_amount = args.request_amount + if request_amount > self.transaction_limit: + # make multiple requests + request_amounts = [] + requests_to_be_made = frappe.utils.ceil(request_amount / self.transaction_limit) # 480/150 = ceil(3.2) = 4 + for i in range(requests_to_be_made): + amount = self.transaction_limit + if i == requests_to_be_made - 1: + amount = request_amount - (self.transaction_limit * i) # for 4th request, 480 - (150 * 3) = 30 + request_amounts.append(amount) + else: + request_amounts = [request_amount] + + return request_amounts def get_account_balance_info(self): payload = dict( @@ -67,7 +88,8 @@ class MpesaSettings(Document): req_name = getattr(response, global_id) error = None - create_request_log(request_dict, "Host", "Mpesa", req_name, error) + if not frappe.db.exists('Integration Request', req_name): + create_request_log(request_dict, "Host", "Mpesa", req_name, error) if error: frappe.throw(_(getattr(response, "errorMessage")), title=_("Transaction Error")) @@ -80,6 +102,8 @@ def generate_stk_push(**kwargs): mpesa_settings = frappe.get_doc("Mpesa Settings", args.payment_gateway[6:]) env = "production" if not mpesa_settings.sandbox else "sandbox" + # for sandbox, business shortcode is same as till number + business_shortcode = mpesa_settings.business_shortcode if env == "production" else mpesa_settings.till_number connector = MpesaConnector(env=env, app_key=mpesa_settings.consumer_key, @@ -87,10 +111,12 @@ def generate_stk_push(**kwargs): mobile_number = sanitize_mobile_number(args.sender) - response = connector.stk_push(business_shortcode=mpesa_settings.till_number, - passcode=mpesa_settings.get_password("online_passkey"), amount=args.grand_total, + response = connector.stk_push( + business_shortcode=business_shortcode, amount=args.request_amount, + passcode=mpesa_settings.get_password("online_passkey"), callback_url=callback_url, reference_code=mpesa_settings.till_number, - phone_number=mobile_number, description="POS Payment") + phone_number=mobile_number, description="POS Payment" + ) return response @@ -108,29 +134,72 @@ def verify_transaction(**kwargs): transaction_response = frappe._dict(kwargs["Body"]["stkCallback"]) checkout_id = getattr(transaction_response, "CheckoutRequestID", "") - request = frappe.get_doc("Integration Request", checkout_id) - transaction_data = frappe._dict(loads(request.data)) + integration_request = frappe.get_doc("Integration Request", checkout_id) + transaction_data = frappe._dict(loads(integration_request.data)) + total_paid = 0 # for multiple integration request made against a pos invoice + success = False # for reporting successfull callback to point of sale ui if transaction_response['ResultCode'] == 0: - if request.reference_doctype and request.reference_docname: + if integration_request.reference_doctype and integration_request.reference_docname: try: - doc = frappe.get_doc(request.reference_doctype, - request.reference_docname) - doc.run_method("on_payment_authorized", 'Completed') - item_response = transaction_response["CallbackMetadata"]["Item"] + amount = fetch_param_value(item_response, "Amount", "Name") mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name") - frappe.db.set_value("POS Invoice", doc.reference_name, "mpesa_receipt_number", mpesa_receipt) - request.handle_success(transaction_response) + pr = frappe.get_doc(integration_request.reference_doctype, integration_request.reference_docname) + + mpesa_receipts, completed_payments = get_completed_integration_requests_info( + integration_request.reference_doctype, + integration_request.reference_docname, + checkout_id + ) + + total_paid = amount + sum(completed_payments) + mpesa_receipts = ', '.join(mpesa_receipts + [mpesa_receipt]) + + if total_paid >= pr.grand_total: + pr.run_method("on_payment_authorized", 'Completed') + success = True + + frappe.db.set_value("POS Invoice", pr.reference_name, "mpesa_receipt_number", mpesa_receipts) + integration_request.handle_success(transaction_response) except Exception: - request.handle_failure(transaction_response) + integration_request.handle_failure(transaction_response) frappe.log_error(frappe.get_traceback()) else: - request.handle_failure(transaction_response) + integration_request.handle_failure(transaction_response) - frappe.publish_realtime('process_phone_payment', doctype="POS Invoice", - docname=transaction_data.payment_reference, user=request.owner, message=transaction_response) + frappe.publish_realtime( + event='process_phone_payment', + doctype="POS Invoice", + docname=transaction_data.payment_reference, + user=integration_request.owner, + message={ + 'amount': total_paid, + 'success': success, + 'failure_message': transaction_response["ResultDesc"] if transaction_response['ResultCode'] != 0 else '' + }, + ) + +def get_completed_integration_requests_info(reference_doctype, reference_docname, checkout_id): + output_of_other_completed_requests = frappe.get_all("Integration Request", filters={ + 'name': ['!=', checkout_id], + 'reference_doctype': reference_doctype, + 'reference_docname': reference_docname, + 'status': 'Completed' + }, pluck="output") + + mpesa_receipts, completed_payments = [], [] + + for out in output_of_other_completed_requests: + out = frappe._dict(loads(out)) + item_response = out["CallbackMetadata"]["Item"] + completed_amount = fetch_param_value(item_response, "Amount", "Name") + completed_mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name") + completed_payments.append(completed_amount) + mpesa_receipts.append(completed_mpesa_receipt) + + return mpesa_receipts, completed_payments def get_account_balance(request_payload): """Call account balance API to send the request to the Mpesa Servers.""" diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py index 49f6d95a6e..29487962f6 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py @@ -9,6 +9,10 @@ from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings import p from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice class TestMpesaSettings(unittest.TestCase): + def tearDown(self): + frappe.db.sql('delete from `tabMpesa Settings`') + frappe.db.sql('delete from `tabIntegration Request` where integration_request_service = "Mpesa"') + def test_creation_of_payment_gateway(self): create_mpesa_settings(payment_gateway_name="_Test") @@ -40,6 +44,8 @@ class TestMpesaSettings(unittest.TestCase): } })) + integration_request.delete() + def test_processing_of_callback_payload(self): create_mpesa_settings(payment_gateway_name="Payment") mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account") @@ -56,10 +62,16 @@ class TestMpesaSettings(unittest.TestCase): # test payment request creation self.assertEquals(pr.payment_gateway, "Mpesa-Payment") - callback_response = get_payment_callback_payload() + # submitting payment request creates integration requests with random id + integration_req_ids = frappe.get_all("Integration Request", filters={ + 'reference_doctype': pr.doctype, + 'reference_docname': pr.name, + }, pluck="name") + + callback_response = get_payment_callback_payload(Amount=500, CheckoutRequestID=integration_req_ids[0]) verify_transaction(**callback_response) # test creation of integration request - integration_request = frappe.get_doc("Integration Request", "ws_CO_061020201133231972") + integration_request = frappe.get_doc("Integration Request", integration_req_ids[0]) # test integration request creation and successful update of the status on receiving callback response self.assertTrue(integration_request) @@ -69,8 +81,120 @@ class TestMpesaSettings(unittest.TestCase): integration_request.reload() self.assertEquals(pos_invoice.mpesa_receipt_number, "LGR7OWQX0R") self.assertEquals(integration_request.status, "Completed") + + frappe.db.set_value("Customer", "_Test Customer", "default_currency", "") + integration_request.delete() + pr.reload() + pr.cancel() + pr.delete() + pos_invoice.delete() + + def test_processing_of_multiple_callback_payload(self): + create_mpesa_settings(payment_gateway_name="Payment") + mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account") + frappe.db.set_value("Account", mpesa_account, "account_currency", "KES") + frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500") + frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES") + + pos_invoice = create_pos_invoice(do_not_submit=1) + pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 1000}) + pos_invoice.contact_mobile = "093456543894" + pos_invoice.currency = "KES" + pos_invoice.save() + + pr = pos_invoice.create_payment_request() + # test payment request creation + self.assertEquals(pr.payment_gateway, "Mpesa-Payment") + + # submitting payment request creates integration requests with random id + integration_req_ids = frappe.get_all("Integration Request", filters={ + 'reference_doctype': pr.doctype, + 'reference_docname': pr.name, + }, pluck="name") + + # create random receipt nos and send it as response to callback handler + mpesa_receipt_numbers = [frappe.utils.random_string(5) for d in integration_req_ids] + + integration_requests = [] + for i in range(len(integration_req_ids)): + callback_response = get_payment_callback_payload( + Amount=500, + CheckoutRequestID=integration_req_ids[i], + MpesaReceiptNumber=mpesa_receipt_numbers[i] + ) + # handle response manually + verify_transaction(**callback_response) + # test completion of integration request + integration_request = frappe.get_doc("Integration Request", integration_req_ids[i]) + self.assertEquals(integration_request.status, "Completed") + integration_requests.append(integration_request) + + # check receipt number once all the integration requests are completed + pos_invoice.reload() + self.assertEquals(pos_invoice.mpesa_receipt_number, ', '.join(mpesa_receipt_numbers)) frappe.db.set_value("Customer", "_Test Customer", "default_currency", "") + [d.delete() for d in integration_requests] + pr.reload() + pr.cancel() + pr.delete() + pos_invoice.delete() + + def test_processing_of_only_one_succes_callback_payload(self): + create_mpesa_settings(payment_gateway_name="Payment") + mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account") + frappe.db.set_value("Account", mpesa_account, "account_currency", "KES") + frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500") + frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES") + + pos_invoice = create_pos_invoice(do_not_submit=1) + pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 1000}) + pos_invoice.contact_mobile = "093456543894" + pos_invoice.currency = "KES" + pos_invoice.save() + + pr = pos_invoice.create_payment_request() + # test payment request creation + self.assertEquals(pr.payment_gateway, "Mpesa-Payment") + + # submitting payment request creates integration requests with random id + integration_req_ids = frappe.get_all("Integration Request", filters={ + 'reference_doctype': pr.doctype, + 'reference_docname': pr.name, + }, pluck="name") + + # create random receipt nos and send it as response to callback handler + mpesa_receipt_numbers = [frappe.utils.random_string(5) for d in integration_req_ids] + + callback_response = get_payment_callback_payload( + Amount=500, + CheckoutRequestID=integration_req_ids[0], + MpesaReceiptNumber=mpesa_receipt_numbers[0] + ) + # handle response manually + verify_transaction(**callback_response) + # test completion of integration request + integration_request = frappe.get_doc("Integration Request", integration_req_ids[0]) + self.assertEquals(integration_request.status, "Completed") + + # now one request is completed + # second integration request fails + # now retrying payment request should make only one integration request again + pr = pos_invoice.create_payment_request() + new_integration_req_ids = frappe.get_all("Integration Request", filters={ + 'reference_doctype': pr.doctype, + 'reference_docname': pr.name, + 'name': ['not in', integration_req_ids] + }, pluck="name") + + self.assertEquals(len(new_integration_req_ids), 1) + + frappe.db.set_value("Customer", "_Test Customer", "default_currency", "") + frappe.db.sql("delete from `tabIntegration Request` where integration_request_service = 'Mpesa'") + pr.reload() + pr.cancel() + pr.delete() + pos_invoice.delete() def create_mpesa_settings(payment_gateway_name="Express"): if frappe.db.exists("Mpesa Settings", payment_gateway_name): @@ -160,16 +284,19 @@ def get_test_account_balance_response(): } } -def get_payment_request_response_payload(): +def get_payment_request_response_payload(Amount=500): """Response received after successfully calling the stk push process request API.""" + + CheckoutRequestID = frappe.utils.random_string(10) + return { "MerchantRequestID": "8071-27184008-1", - "CheckoutRequestID": "ws_CO_061020201133231972", + "CheckoutRequestID": CheckoutRequestID, "ResultCode": 0, "ResultDesc": "The service request is processed successfully.", "CallbackMetadata": { "Item": [ - { "Name": "Amount", "Value": 500.0 }, + { "Name": "Amount", "Value": Amount }, { "Name": "MpesaReceiptNumber", "Value": "LGR7OWQX0R" }, { "Name": "TransactionDate", "Value": 20201006113336 }, { "Name": "PhoneNumber", "Value": 254723575670 } @@ -177,41 +304,26 @@ def get_payment_request_response_payload(): } } - -def get_payment_callback_payload(): +def get_payment_callback_payload(Amount=500, CheckoutRequestID="ws_CO_061020201133231972", MpesaReceiptNumber="LGR7OWQX0R"): """Response received from the server as callback after calling the stkpush process request API.""" return { "Body":{ - "stkCallback":{ - "MerchantRequestID":"19465-780693-1", - "CheckoutRequestID":"ws_CO_061020201133231972", - "ResultCode":0, - "ResultDesc":"The service request is processed successfully.", - "CallbackMetadata":{ - "Item":[ - { - "Name":"Amount", - "Value":500 - }, - { - "Name":"MpesaReceiptNumber", - "Value":"LGR7OWQX0R" - }, - { - "Name":"Balance" - }, - { - "Name":"TransactionDate", - "Value":20170727154800 - }, - { - "Name":"PhoneNumber", - "Value":254721566839 + "stkCallback":{ + "MerchantRequestID":"19465-780693-1", + "CheckoutRequestID":CheckoutRequestID, + "ResultCode":0, + "ResultDesc":"The service request is processed successfully.", + "CallbackMetadata":{ + "Item":[ + { "Name":"Amount", "Value":Amount }, + { "Name":"MpesaReceiptNumber", "Value":MpesaReceiptNumber }, + { "Name":"Balance" }, + { "Name":"TransactionDate", "Value":20170727154800 }, + { "Name":"PhoneNumber", "Value":254721566839 } + ] } - ] } } - } } def get_account_balance_callback_payload(): diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py index 66d0e5f77d..5f990cdd03 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py @@ -20,7 +20,7 @@ class PlaidConnector(): client_id=self.settings.plaid_client_id, secret=self.settings.get_password("plaid_secret"), environment=self.settings.plaid_env, - api_version="2019-05-29" + api_version="2020-09-14" ) def get_access_token(self, public_token): @@ -29,7 +29,7 @@ class PlaidConnector(): response = self.client.Item.public_token.exchange(public_token) access_token = response["access_token"] return access_token - + def get_token_request(self, update_mode=False): country_codes = ["US", "CA", "FR", "IE", "NL", "ES", "GB"] if self.settings.enable_european_access else ["US", "CA"] args = { diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index 70c7f3fe5d..21f6fee79c 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -204,8 +204,8 @@ def new_bank_transaction(transaction): "date": getdate(transaction["date"]), "status": status, "bank_account": bank_account, - "debit": debit, - "credit": credit, + "deposit": debit, + "withdrawal": credit, "currency": transaction["iso_currency_code"], "transaction_id": transaction["transaction_id"], "reference_number": transaction["payment_meta"]["reference_number"], diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.js b/erpnext/healthcare/doctype/appointment_type/appointment_type.js index 15916a5134..861675acea 100644 --- a/erpnext/healthcare/doctype/appointment_type/appointment_type.js +++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.js @@ -2,4 +2,82 @@ // For license information, please see license.txt frappe.ui.form.on('Appointment Type', { + refresh: function(frm) { + frm.set_query('price_list', function() { + return { + filters: {'selling': 1} + }; + }); + + frm.set_query('medical_department', 'items', function(doc) { + let item_list = doc.items.map(({medical_department}) => medical_department); + return { + filters: [ + ['Medical Department', 'name', 'not in', item_list] + ] + }; + }); + + frm.set_query('op_consulting_charge_item', 'items', function() { + return { + filters: { + is_stock_item: 0 + } + }; + }); + + frm.set_query('inpatient_visit_charge_item', 'items', function() { + return { + filters: { + is_stock_item: 0 + } + }; + }); + } }); + +frappe.ui.form.on('Appointment Type Service Item', { + op_consulting_charge_item: function(frm, cdt, cdn) { + let d = locals[cdt][cdn]; + if (frm.doc.price_list && d.op_consulting_charge_item) { + frappe.call({ + 'method': 'frappe.client.get_value', + args: { + 'doctype': 'Item Price', + 'filters': { + 'item_code': d.op_consulting_charge_item, + 'price_list': frm.doc.price_list + }, + 'fieldname': ['price_list_rate'] + }, + callback: function(data) { + if (data.message.price_list_rate) { + frappe.model.set_value(cdt, cdn, 'op_consulting_charge', data.message.price_list_rate); + } + } + }); + } + }, + + inpatient_visit_charge_item: function(frm, cdt, cdn) { + let d = locals[cdt][cdn]; + if (frm.doc.price_list && d.inpatient_visit_charge_item) { + frappe.call({ + 'method': 'frappe.client.get_value', + args: { + 'doctype': 'Item Price', + 'filters': { + 'item_code': d.inpatient_visit_charge_item, + 'price_list': frm.doc.price_list + }, + 'fieldname': ['price_list_rate'] + }, + callback: function (data) { + if (data.message.price_list_rate) { + frappe.model.set_value(cdt, cdn, 'inpatient_visit_charge', data.message.price_list_rate); + } + } + }); + } + } +}); \ No newline at end of file diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.json b/erpnext/healthcare/doctype/appointment_type/appointment_type.json index 58753bb4f0..3872318287 100644 --- a/erpnext/healthcare/doctype/appointment_type/appointment_type.json +++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.json @@ -12,7 +12,10 @@ "appointment_type", "ip", "default_duration", - "color" + "color", + "billing_section", + "price_list", + "items" ], "fields": [ { @@ -52,10 +55,27 @@ "label": "Color", "no_copy": 1, "report_hide": 1 + }, + { + "fieldname": "billing_section", + "fieldtype": "Section Break", + "label": "Billing" + }, + { + "fieldname": "price_list", + "fieldtype": "Link", + "label": "Price List", + "options": "Price List" + }, + { + "fieldname": "items", + "fieldtype": "Table", + "label": "Appointment Type Service Items", + "options": "Appointment Type Service Item" } ], "links": [], - "modified": "2020-02-03 21:06:05.833050", + "modified": "2021-01-22 09:41:05.010524", "modified_by": "Administrator", "module": "Healthcare", "name": "Appointment Type", diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.py b/erpnext/healthcare/doctype/appointment_type/appointment_type.py index 1dacffab35..67a24f31e0 100644 --- a/erpnext/healthcare/doctype/appointment_type/appointment_type.py +++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.py @@ -4,6 +4,53 @@ from __future__ import unicode_literals from frappe.model.document import Document +import frappe class AppointmentType(Document): - pass + def validate(self): + if self.items and self.price_list: + for item in self.items: + existing_op_item_price = frappe.db.exists('Item Price', { + 'item_code': item.op_consulting_charge_item, + 'price_list': self.price_list + }) + + if not existing_op_item_price and item.op_consulting_charge_item and item.op_consulting_charge: + make_item_price(self.price_list, item.op_consulting_charge_item, item.op_consulting_charge) + + existing_ip_item_price = frappe.db.exists('Item Price', { + 'item_code': item.inpatient_visit_charge_item, + 'price_list': self.price_list + }) + + if not existing_ip_item_price and item.inpatient_visit_charge_item and item.inpatient_visit_charge: + make_item_price(self.price_list, item.inpatient_visit_charge_item, item.inpatient_visit_charge) + +@frappe.whitelist() +def get_service_item_based_on_department(appointment_type, department): + item_list = frappe.db.get_value('Appointment Type Service Item', + filters = {'medical_department': department, 'parent': appointment_type}, + fieldname = ['op_consulting_charge_item', + 'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'], + as_dict = 1 + ) + + # if department wise items are not set up + # use the generic items + if not item_list: + item_list = frappe.db.get_value('Appointment Type Service Item', + filters = {'parent': appointment_type}, + fieldname = ['op_consulting_charge_item', + 'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'], + as_dict = 1 + ) + + return item_list + +def make_item_price(price_list, item, item_price): + frappe.get_doc({ + 'doctype': 'Item Price', + 'price_list': price_list, + 'item_code': item, + 'price_list_rate': item_price + }).insert(ignore_permissions=True, ignore_mandatory=True) diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/__init__.py b/erpnext/healthcare/doctype/appointment_type_service_item/__init__.py similarity index 100% rename from erpnext/accounts/doctype/bank_statement_transaction_entry/__init__.py rename to erpnext/healthcare/doctype/appointment_type_service_item/__init__.py diff --git a/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json new file mode 100644 index 0000000000..5ff68cd682 --- /dev/null +++ b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json @@ -0,0 +1,67 @@ +{ + "actions": [], + "creation": "2021-01-22 09:34:53.373105", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "medical_department", + "op_consulting_charge_item", + "op_consulting_charge", + "column_break_4", + "inpatient_visit_charge_item", + "inpatient_visit_charge" + ], + "fields": [ + { + "fieldname": "medical_department", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Medical Department", + "options": "Medical Department" + }, + { + "fieldname": "op_consulting_charge_item", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Out Patient Consulting Charge Item", + "options": "Item" + }, + { + "fieldname": "op_consulting_charge", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Out Patient Consulting Charge" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "inpatient_visit_charge_item", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Inpatient Visit Charge Item", + "options": "Item" + }, + { + "fieldname": "inpatient_visit_charge", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Inpatient Visit Charge Item" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-01-22 09:35:26.503443", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Appointment Type Service Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.py b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py similarity index 56% rename from erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.py rename to erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py index cb1b15815f..b2e0e82bad 100644 --- a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.py +++ b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2017, sathishpy@gmail.com and contributors +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document -class BankStatementTransactionInvoiceItem(Document): +class AppointmentTypeServiceItem(Document): pass diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index c324228467..325c2094fb 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -121,6 +121,7 @@ class ClinicalProcedure(Document): stock_entry.stock_entry_type = 'Material Receipt' stock_entry.to_warehouse = self.warehouse + stock_entry.company = self.company expense_account = get_account(None, 'expense_account', 'Healthcare Settings', self.company) for item in self.items: if item.qty > item.actual_qty: diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py index 4ee5f6bad3..fb72073a07 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- + # -*- coding: utf-8 -*- # Copyright (c) 2017, ESS LLP and Contributors # See license.txt from __future__ import unicode_literals @@ -60,6 +60,7 @@ def create_procedure(procedure_template, patient, practitioner): procedure.practitioner = practitioner procedure.consume_stock = procedure_template.allow_stock_consumption procedure.items = procedure_template.items - procedure.warehouse = frappe.db.get_single_value('Stock Settings', 'default_warehouse') + procedure.company = "_Test Company" + procedure.warehouse = "_Test Warehouse - _TC" procedure.submit() return procedure \ No newline at end of file diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json index cb747f95ef..8162f03f6d 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json @@ -159,6 +159,7 @@ "fieldname": "op_consulting_charge", "fieldtype": "Currency", "label": "Out Patient Consulting Charge", + "mandatory_depends_on": "op_consulting_charge_item", "options": "Currency" }, { @@ -174,7 +175,8 @@ { "fieldname": "inpatient_visit_charge", "fieldtype": "Currency", - "label": "Inpatient Visit Charge" + "label": "Inpatient Visit Charge", + "mandatory_depends_on": "inpatient_visit_charge_item" }, { "depends_on": "eval: !doc.__islocal", @@ -280,7 +282,7 @@ ], "image_field": "image", "links": [], - "modified": "2020-04-06 13:44:24.759623", + "modified": "2021-01-22 10:14:43.187675", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Practitioner", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index 3d5073b13e..0354733dfb 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -24,11 +24,13 @@ frappe.ui.form.on('Patient Appointment', { }); frm.set_query('practitioner', function() { - return { - filters: { - 'department': frm.doc.department - } - }; + if (frm.doc.department) { + return { + filters: { + 'department': frm.doc.department + } + }; + } }); frm.set_query('service_unit', function() { @@ -140,6 +142,20 @@ frappe.ui.form.on('Patient Appointment', { patient: function(frm) { if (frm.doc.patient) { frm.trigger('toggle_payment_fields'); + frappe.call({ + method: 'frappe.client.get', + args: { + doctype: 'Patient', + name: frm.doc.patient + }, + callback: function (data) { + let age = null; + if (data.message.dob) { + age = calculate_age(data.message.dob); + } + frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', age); + } + }); } else { frm.set_value('patient_name', ''); frm.set_value('patient_sex', ''); @@ -148,6 +164,37 @@ frappe.ui.form.on('Patient Appointment', { } }, + practitioner: function(frm) { + if (frm.doc.practitioner ) { + frm.events.set_payment_details(frm); + } + }, + + appointment_type: function(frm) { + if (frm.doc.appointment_type) { + frm.events.set_payment_details(frm); + } + }, + + set_payment_details: function(frm) { + frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing').then(val => { + if (val) { + frappe.call({ + method: 'erpnext.healthcare.utils.get_service_item_and_practitioner_charge', + args: { + doc: frm.doc + }, + callback: function(data) { + if (data.message) { + frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.practitioner_charge); + frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.service_item); + } + } + }); + } + }); + }, + therapy_plan: function(frm) { frm.trigger('set_therapy_type_filter'); }, @@ -190,14 +237,18 @@ frappe.ui.form.on('Patient Appointment', { // show payment fields as non-mandatory frm.toggle_display('mode_of_payment', 0); frm.toggle_display('paid_amount', 0); + frm.toggle_display('billing_item', 0); frm.toggle_reqd('mode_of_payment', 0); frm.toggle_reqd('paid_amount', 0); + frm.toggle_reqd('billing_item', 0); } else { // if automated appointment invoicing is disabled, hide fields frm.toggle_display('mode_of_payment', data.message ? 1 : 0); frm.toggle_display('paid_amount', data.message ? 1 : 0); + frm.toggle_display('billing_item', data.message ? 1 : 0); frm.toggle_reqd('mode_of_payment', data.message ? 1 : 0); frm.toggle_reqd('paid_amount', data.message ? 1 :0); + frm.toggle_reqd('billing_item', data.message ? 1 : 0); } } }); @@ -540,57 +591,6 @@ let update_status = function(frm, status){ ); }; -frappe.ui.form.on('Patient Appointment', 'practitioner', function(frm) { - if (frm.doc.practitioner) { - frappe.call({ - method: 'frappe.client.get', - args: { - doctype: 'Healthcare Practitioner', - name: frm.doc.practitioner - }, - callback: function (data) { - frappe.model.set_value(frm.doctype, frm.docname, 'department', data.message.department); - frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.op_consulting_charge); - frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.op_consulting_charge_item); - } - }); - } -}); - -frappe.ui.form.on('Patient Appointment', 'patient', function(frm) { - if (frm.doc.patient) { - frappe.call({ - method: 'frappe.client.get', - args: { - doctype: 'Patient', - name: frm.doc.patient - }, - callback: function (data) { - let age = null; - if (data.message.dob) { - age = calculate_age(data.message.dob); - } - frappe.model.set_value(frm.doctype,frm.docname, 'patient_age', age); - } - }); - } -}); - -frappe.ui.form.on('Patient Appointment', 'appointment_type', function(frm) { - if (frm.doc.appointment_type) { - frappe.call({ - method: 'frappe.client.get', - args: { - doctype: 'Appointment Type', - name: frm.doc.appointment_type - }, - callback: function(data) { - frappe.model.set_value(frm.doctype,frm.docname, 'duration',data.message.default_duration); - } - }); - } -}); - let calculate_age = function(birth) { let ageMS = Date.parse(Date()) - Date.parse(birth); let age = new Date(); diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index 35600e4809..83c92af36a 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -19,19 +19,19 @@ "inpatient_record", "column_break_1", "company", + "practitioner", + "practitioner_name", + "department", "service_unit", + "section_break_12", + "appointment_type", + "duration", "procedure_template", "get_procedure_from_encounter", "procedure_prescription", "therapy_plan", "therapy_type", "get_prescribed_therapies", - "practitioner", - "practitioner_name", - "department", - "section_break_12", - "appointment_type", - "duration", "column_break_17", "appointment_date", "appointment_time", @@ -79,6 +79,7 @@ "set_only_once": 1 }, { + "fetch_from": "appointment_type.default_duration", "fieldname": "duration", "fieldtype": "Int", "in_filter": 1, @@ -144,7 +145,6 @@ "in_standard_filter": 1, "label": "Healthcare Practitioner", "options": "Healthcare Practitioner", - "read_only": 1, "reqd": 1, "search_index": 1, "set_only_once": 1 @@ -158,7 +158,6 @@ "in_standard_filter": 1, "label": "Department", "options": "Medical Department", - "read_only": 1, "search_index": 1, "set_only_once": 1 }, @@ -227,12 +226,14 @@ "fieldname": "mode_of_payment", "fieldtype": "Link", "label": "Mode of Payment", - "options": "Mode of Payment" + "options": "Mode of Payment", + "read_only_depends_on": "invoiced" }, { "fieldname": "paid_amount", "fieldtype": "Currency", - "label": "Paid Amount" + "label": "Paid Amount", + "read_only_depends_on": "invoiced" }, { "fieldname": "column_break_2", @@ -302,7 +303,8 @@ "fieldname": "therapy_plan", "fieldtype": "Link", "label": "Therapy Plan", - "options": "Therapy Plan" + "options": "Therapy Plan", + "set_only_once": 1 }, { "fieldname": "ref_sales_invoice", @@ -347,7 +349,7 @@ } ], "links": [], - "modified": "2020-12-16 13:16:58.578503", + "modified": "2021-02-08 13:13:15.116833", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index f2b94b8e9c..1f76cd624c 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -26,6 +26,7 @@ class PatientAppointment(Document): def after_insert(self): self.update_prescription_details() + self.set_payment_details() invoice_appointment(self) self.update_fee_validity() send_confirmation_msg(self) @@ -85,6 +86,13 @@ class PatientAppointment(Document): def set_appointment_datetime(self): self.appointment_datetime = "%s %s" % (self.appointment_date, self.appointment_time or "00:00:00") + def set_payment_details(self): + if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'): + details = get_service_item_and_practitioner_charge(self) + self.db_set('billing_item', details.get('service_item')) + if not self.paid_amount: + self.db_set('paid_amount', details.get('practitioner_charge')) + def validate_customer_created(self): if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'): if not frappe.db.get_value('Patient', self.patient, 'customer'): @@ -148,31 +156,37 @@ def invoice_appointment(appointment_doc): fee_validity = None if automate_invoicing and not appointment_invoiced and not fee_validity: - sales_invoice = frappe.new_doc('Sales Invoice') - sales_invoice.patient = appointment_doc.patient - sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer') - sales_invoice.appointment = appointment_doc.name - sales_invoice.due_date = getdate() - sales_invoice.company = appointment_doc.company - sales_invoice.debit_to = get_receivable_account(appointment_doc.company) + create_sales_invoice(appointment_doc) - item = sales_invoice.append('items', {}) - item = get_appointment_item(appointment_doc, item) - # Add payments if payment details are supplied else proceed to create invoice as Unpaid - if appointment_doc.mode_of_payment and appointment_doc.paid_amount: - sales_invoice.is_pos = 1 - payment = sales_invoice.append('payments', {}) - payment.mode_of_payment = appointment_doc.mode_of_payment - payment.amount = appointment_doc.paid_amount +def create_sales_invoice(appointment_doc): + sales_invoice = frappe.new_doc('Sales Invoice') + sales_invoice.patient = appointment_doc.patient + sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer') + sales_invoice.appointment = appointment_doc.name + sales_invoice.due_date = getdate() + sales_invoice.company = appointment_doc.company + sales_invoice.debit_to = get_receivable_account(appointment_doc.company) - sales_invoice.set_missing_values(for_validate=True) - sales_invoice.flags.ignore_mandatory = True - sales_invoice.save(ignore_permissions=True) - sales_invoice.submit() - frappe.msgprint(_('Sales Invoice {0} created').format(sales_invoice.name), alert=True) - frappe.db.set_value('Patient Appointment', appointment_doc.name, 'invoiced', 1) - frappe.db.set_value('Patient Appointment', appointment_doc.name, 'ref_sales_invoice', sales_invoice.name) + item = sales_invoice.append('items', {}) + item = get_appointment_item(appointment_doc, item) + + # Add payments if payment details are supplied else proceed to create invoice as Unpaid + if appointment_doc.mode_of_payment and appointment_doc.paid_amount: + sales_invoice.is_pos = 1 + payment = sales_invoice.append('payments', {}) + payment.mode_of_payment = appointment_doc.mode_of_payment + payment.amount = appointment_doc.paid_amount + + sales_invoice.set_missing_values(for_validate=True) + sales_invoice.flags.ignore_mandatory = True + sales_invoice.save(ignore_permissions=True) + sales_invoice.submit() + frappe.msgprint(_('Sales Invoice {0} created').format(sales_invoice.name), alert=True) + frappe.db.set_value('Patient Appointment', appointment_doc.name, { + 'invoiced': 1, + 'ref_sales_invoice': sales_invoice.name + }) def check_is_new_patient(patient, name=None): @@ -187,13 +201,14 @@ def check_is_new_patient(patient, name=None): def get_appointment_item(appointment_doc, item): - service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment_doc) - item.item_code = service_item + details = get_service_item_and_practitioner_charge(appointment_doc) + charge = appointment_doc.paid_amount or details.get('practitioner_charge') + item.item_code = details.get('service_item') item.description = _('Consulting Charges: {0}').format(appointment_doc.practitioner) item.income_account = get_income_account(appointment_doc.practitioner, appointment_doc.company) item.cost_center = frappe.get_cached_value('Company', appointment_doc.company, 'cost_center') - item.rate = practitioner_charge - item.amount = practitioner_charge + item.rate = charge + item.amount = charge item.qty = 1 item.reference_dt = 'Patient Appointment' item.reference_dn = appointment_doc.name diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py index f7ec6f58fc..2bb8a53c45 100644 --- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py @@ -32,7 +32,8 @@ class TestPatientAppointment(unittest.TestCase): patient, medical_department, practitioner = create_healthcare_docs() frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4), invoice = 1) - self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 1) + appointment.reload() + self.assertEqual(appointment.invoiced, 1) encounter = make_encounter(appointment.name) self.assertTrue(encounter) self.assertEqual(encounter.company, appointment.company) @@ -41,7 +42,7 @@ class TestPatientAppointment(unittest.TestCase): # invoiced flag mapped from appointment self.assertEqual(encounter.invoiced, frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced')) - def test_invoicing(self): + def test_auto_invoicing(self): patient, medical_department, practitioner = create_healthcare_docs() frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0) frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0) @@ -57,6 +58,50 @@ class TestPatientAppointment(unittest.TestCase): self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'patient'), appointment.patient) self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount) + def test_auto_invoicing_based_on_department(self): + patient, medical_department, practitioner = create_healthcare_docs() + frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0) + frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) + appointment_type = create_appointment_type() + + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), + invoice=1, appointment_type=appointment_type.name, department='_Test Medical Department') + appointment.reload() + + self.assertEqual(appointment.invoiced, 1) + self.assertEqual(appointment.billing_item, 'HLC-SI-001') + self.assertEqual(appointment.paid_amount, 200) + + sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent') + self.assertTrue(sales_invoice_name) + self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount) + + def test_auto_invoicing_according_to_appointment_type_charge(self): + patient, medical_department, practitioner = create_healthcare_docs() + frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0) + frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) + + item = create_healthcare_service_items() + items = [{ + 'op_consulting_charge_item': item, + 'op_consulting_charge': 300 + }] + appointment_type = create_appointment_type(args={ + 'name': 'Generic Appointment Type charge', + 'items': items + }) + + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), + invoice=1, appointment_type=appointment_type.name) + appointment.reload() + + self.assertEqual(appointment.invoiced, 1) + self.assertEqual(appointment.billing_item, item) + self.assertEqual(appointment.paid_amount, 300) + + sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent') + self.assertTrue(sales_invoice_name) + def test_appointment_cancel(self): patient, medical_department, practitioner = create_healthcare_docs() frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 1) @@ -178,14 +223,15 @@ def create_encounter(appointment): encounter.submit() return encounter -def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0, service_unit=None, save=1): +def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0, + service_unit=None, appointment_type=None, save=1, department=None): item = create_healthcare_service_items() frappe.db.set_value('Healthcare Settings', None, 'inpatient_visit_charge_item', item) frappe.db.set_value('Healthcare Settings', None, 'op_consulting_charge_item', item) appointment = frappe.new_doc('Patient Appointment') appointment.patient = patient appointment.practitioner = practitioner - appointment.department = '_Test Medical Department' + appointment.department = department or '_Test Medical Department' appointment.appointment_date = appointment_date appointment.company = '_Test Company' appointment.duration = 15 @@ -193,7 +239,8 @@ def create_appointment(patient, practitioner, appointment_date, invoice=0, proce appointment.service_unit = service_unit if invoice: appointment.mode_of_payment = 'Cash' - appointment.paid_amount = 500 + if appointment_type: + appointment.appointment_type = appointment_type if procedure_template: appointment.procedure_template = create_clinical_procedure_template().get('name') if save: @@ -223,4 +270,29 @@ def create_clinical_procedure_template(): template.description = 'Knee Surgery and Rehab' template.rate = 50000 template.save() - return template \ No newline at end of file + return template + +def create_appointment_type(args=None): + if not args: + args = frappe.local.form_dict + + name = args.get('name') or 'Test Appointment Type wise Charge' + + if frappe.db.exists('Appointment Type', name): + return frappe.get_doc('Appointment Type', name) + + else: + item = create_healthcare_service_items() + items = [{ + 'medical_department': '_Test Medical Department', + 'op_consulting_charge_item': item, + 'op_consulting_charge': 200 + }] + return frappe.get_doc({ + 'doctype': 'Appointment Type', + 'appointment_type': args.get('name') or 'Test Appointment Type wise Charge', + 'default_duration': args.get('default_duration') or 20, + 'color': args.get('color') or '#7575ff', + 'price_list': args.get('price_list') or frappe.db.get_value("Price List", {"selling": 1}), + 'items': args.get('items') or items + }).insert() \ No newline at end of file diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index d4027dff4e..d3d22c80b6 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -5,9 +5,11 @@ from __future__ import unicode_literals import math import frappe +import json from frappe import _ from frappe.utils.formatters import format_value from frappe.utils import time_diff_in_hours, rounded +from six import string_types from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple @@ -64,7 +66,9 @@ def get_appointments_to_invoice(patient, company): income_account = None service_item = None if appointment.practitioner: - service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment) + details = get_service_item_and_practitioner_charge(appointment) + service_item = details.get('service_item') + practitioner_charge = details.get('practitioner_charge') income_account = get_income_account(appointment.practitioner, appointment.company) appointments_to_invoice.append({ 'reference_type': 'Patient Appointment', @@ -97,7 +101,9 @@ def get_encounters_to_invoice(patient, company): frappe.db.get_single_value('Healthcare Settings', 'do_not_bill_inpatient_encounters'): continue - service_item, practitioner_charge = get_service_item_and_practitioner_charge(encounter) + details = get_service_item_and_practitioner_charge(encounter) + service_item = details.get('service_item') + practitioner_charge = details.get('practitioner_charge') income_account = get_income_account(encounter.practitioner, encounter.company) encounters_to_invoice.append({ @@ -173,7 +179,7 @@ def get_clinical_procedures_to_invoice(patient, company): if procedure.invoice_separately_as_consumables and procedure.consume_stock \ and procedure.status == 'Completed' and not procedure.consumption_invoiced: - service_item = get_healthcare_service_item('clinical_procedure_consumable_item') + service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item') if not service_item: msg = _('Please Configure Clinical Procedure Consumable Item in ') msg += '''Healthcare Settings''' @@ -304,24 +310,50 @@ def get_therapy_sessions_to_invoice(patient, company): return therapy_sessions_to_invoice - +@frappe.whitelist() def get_service_item_and_practitioner_charge(doc): + if isinstance(doc, string_types): + doc = json.loads(doc) + doc = frappe.get_doc(doc) + + service_item = None + practitioner_charge = None + department = doc.medical_department if doc.doctype == 'Patient Encounter' else doc.department + is_inpatient = doc.inpatient_record - if is_inpatient: - service_item = get_practitioner_service_item(doc.practitioner, 'inpatient_visit_charge_item') + + if doc.get('appointment_type'): + service_item, practitioner_charge = get_appointment_type_service_item(doc.appointment_type, department, is_inpatient) + + if not service_item and not practitioner_charge: + service_item, practitioner_charge = get_practitioner_service_item(doc.practitioner, is_inpatient) if not service_item: - service_item = get_healthcare_service_item('inpatient_visit_charge_item') - else: - service_item = get_practitioner_service_item(doc.practitioner, 'op_consulting_charge_item') - if not service_item: - service_item = get_healthcare_service_item('op_consulting_charge_item') + service_item = get_healthcare_service_item(is_inpatient) + if not service_item: throw_config_service_item(is_inpatient) - practitioner_charge = get_practitioner_charge(doc.practitioner, is_inpatient) if not practitioner_charge: throw_config_practitioner_charge(is_inpatient, doc.practitioner) + return {'service_item': service_item, 'practitioner_charge': practitioner_charge} + + +def get_appointment_type_service_item(appointment_type, department, is_inpatient): + from erpnext.healthcare.doctype.appointment_type.appointment_type import get_service_item_based_on_department + + item_list = get_service_item_based_on_department(appointment_type, department) + service_item = None + practitioner_charge = None + + if item_list: + if is_inpatient: + service_item = item_list.get('inpatient_visit_charge_item') + practitioner_charge = item_list.get('inpatient_visit_charge') + else: + service_item = item_list.get('op_consulting_charge_item') + practitioner_charge = item_list.get('op_consulting_charge') + return service_item, practitioner_charge @@ -345,12 +377,27 @@ def throw_config_practitioner_charge(is_inpatient, practitioner): frappe.throw(msg, title=_('Missing Configuration')) -def get_practitioner_service_item(practitioner, service_item_field): - return frappe.db.get_value('Healthcare Practitioner', practitioner, service_item_field) +def get_practitioner_service_item(practitioner, is_inpatient): + service_item = None + practitioner_charge = None + + if is_inpatient: + service_item, practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, ['inpatient_visit_charge_item', 'inpatient_visit_charge']) + else: + service_item, practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, ['op_consulting_charge_item', 'op_consulting_charge']) + + return service_item, practitioner_charge -def get_healthcare_service_item(service_item_field): - return frappe.db.get_single_value('Healthcare Settings', service_item_field) +def get_healthcare_service_item(is_inpatient): + service_item = None + + if is_inpatient: + service_item = frappe.db.get_single_value('Healthcare Settings', 'inpatient_visit_charge_item') + else: + service_item = frappe.db.get_single_value('Healthcare Settings', 'op_consulting_charge_item') + + return service_item def get_practitioner_charge(practitioner, is_inpatient): @@ -381,7 +428,8 @@ def set_invoiced(item, method, ref_invoice=None): invoiced = True if item.reference_dt == 'Clinical Procedure': - if get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code: + service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item') + if service_item == item.item_code: frappe.db.set_value(item.reference_dt, item.reference_dn, 'consumption_invoiced', invoiced) else: frappe.db.set_value(item.reference_dt, item.reference_dn, 'invoiced', invoiced) @@ -403,7 +451,8 @@ def set_invoiced(item, method, ref_invoice=None): def validate_invoiced_on_submit(item): - if item.reference_dt == 'Clinical Procedure' and get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code: + if item.reference_dt == 'Clinical Procedure' and \ + frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item') == item.item_code: is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'consumption_invoiced') else: is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'invoiced') diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 109d9216e7..39d3659b2b 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -272,6 +272,9 @@ doc_events = { 'Address': { 'validate': ['erpnext.regional.india.utils.validate_gstin_for_india', 'erpnext.regional.italy.utils.set_state_code', 'erpnext.regional.india.utils.update_gst_category'] }, + 'Supplier': { + 'validate': 'erpnext.regional.india.utils.validate_pan_for_india' + }, ('Sales Invoice', 'Sales Order', 'Delivery Note', 'Purchase Invoice', 'Purchase Order', 'Purchase Receipt'): { 'validate': ['erpnext.regional.india.utils.set_place_of_supply'] }, @@ -399,6 +402,7 @@ regional_overrides = { 'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_header': 'erpnext.regional.india.utils.get_itemised_tax_breakup_header', 'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_data': 'erpnext.regional.india.utils.get_itemised_tax_breakup_data', 'erpnext.accounts.party.get_regional_address_details': 'erpnext.regional.india.utils.get_regional_address_details', + 'erpnext.controllers.taxes_and_totals.get_regional_round_off_accounts': 'erpnext.regional.india.utils.get_regional_round_off_accounts', 'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption', 'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period', 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries', diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html index 6324b04927..9f667a6835 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html +++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html @@ -4,11 +4,11 @@ {{ __("Leave Type") }} - {{ __("Total Allocated Leaves") }} - {{ __("Expired Leaves") }} - {{ __("Used Leaves") }} - {{ __("Pending Leaves") }} - {{ __("Available Leaves") }} + {{ __("Total Allocated Leave") }} + {{ __("Expired Leave") }} + {{ __("Used Leave") }} + {{ __("Pending Leave") }} + {{ __("Available Leave") }} @@ -25,5 +25,5 @@ {% else %} -

    No Leaves have been allocated.

    -{% endif %} \ No newline at end of file +

    No Leave has been allocated.

    +{% endif %} diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index 1b92358184..06f9160363 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -40,17 +40,17 @@ def get_columns(): 'fieldname': 'opening_balance', 'width': 130, }, { - 'label': _('Leaves Allocated'), + 'label': _('Leave Allocated'), 'fieldtype': 'float', 'fieldname': 'leaves_allocated', 'width': 130, }, { - 'label': _('Leaves Taken'), + 'label': _('Leave Taken'), 'fieldtype': 'float', 'fieldname': 'leaves_taken', 'width': 130, }, { - 'label': _('Leaves Expired'), + 'label': _('Leave Expired'), 'fieldtype': 'float', 'fieldname': 'leaves_expired', 'width': 130, diff --git a/erpnext/loan_management/dashboard_chart/loan_disbursements/loan_disbursements.json b/erpnext/loan_management/dashboard_chart/loan_disbursements/loan_disbursements.json new file mode 100644 index 0000000000..b8abf210f8 --- /dev/null +++ b/erpnext/loan_management/dashboard_chart/loan_disbursements/loan_disbursements.json @@ -0,0 +1,29 @@ +{ + "based_on": "disbursement_date", + "chart_name": "Loan Disbursements", + "chart_type": "Sum", + "creation": "2021-02-06 18:40:36.148470", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Loan Disbursement", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Disbursement\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_type": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "modified": "2021-02-06 18:40:49.308663", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Disbursements", + "number_of_groups": 0, + "owner": "Administrator", + "source": "", + "time_interval": "Daily", + "timeseries": 1, + "timespan": "Last Month", + "type": "Line", + "use_report_chart": 0, + "value_based_on": "disbursed_amount", + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json b/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json new file mode 100644 index 0000000000..aa0f78a2f6 --- /dev/null +++ b/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json @@ -0,0 +1,31 @@ +{ + "based_on": "posting_date", + "chart_name": "Loan Interest Accrual", + "chart_type": "Sum", + "color": "#39E4A5", + "creation": "2021-02-18 20:07:04.843876", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Loan Interest Accrual", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Interest Accrual\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_type": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "last_synced_on": "2021-02-21 21:01:26.022634", + "modified": "2021-02-21 21:01:44.930712", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Interest Accrual", + "number_of_groups": 0, + "owner": "Administrator", + "source": "", + "time_interval": "Monthly", + "timeseries": 1, + "timespan": "Last Year", + "type": "Line", + "use_report_chart": 0, + "value_based_on": "interest_amount", + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json b/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json new file mode 100644 index 0000000000..35bd43b994 --- /dev/null +++ b/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json @@ -0,0 +1,31 @@ +{ + "based_on": "creation", + "chart_name": "New Loans", + "chart_type": "Count", + "color": "#449CF0", + "creation": "2021-02-06 16:59:27.509170", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Loan", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false]]", + "group_by_type": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "last_synced_on": "2021-02-21 20:55:33.515025", + "modified": "2021-02-21 21:00:33.900821", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "New Loans", + "number_of_groups": 0, + "owner": "Administrator", + "source": "", + "time_interval": "Daily", + "timeseries": 1, + "timespan": "Last Month", + "type": "Bar", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json b/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json new file mode 100644 index 0000000000..76c27b062d --- /dev/null +++ b/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json @@ -0,0 +1,31 @@ +{ + "based_on": "", + "chart_name": "Top 10 Pledged Loan Securities", + "chart_type": "Custom", + "color": "#EC864B", + "creation": "2021-02-06 22:02:46.284479", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "", + "dynamic_filters_json": "[]", + "filters_json": "[]", + "group_by_type": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "last_synced_on": "2021-02-21 21:00:57.043034", + "modified": "2021-02-21 21:01:10.048623", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Top 10 Pledged Loan Securities", + "number_of_groups": 0, + "owner": "Administrator", + "source": "Top 10 Pledged Loan Securities", + "time_interval": "Yearly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 0, + "value_based_on": "", + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/__init__.py b/erpnext/loan_management/dashboard_chart_source/__init__.py similarity index 100% rename from erpnext/accounts/doctype/bank_statement_transaction_invoice_item/__init__.py rename to erpnext/loan_management/dashboard_chart_source/__init__.py diff --git a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/__init__.py b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/__init__.py similarity index 100% rename from erpnext/accounts/doctype/bank_statement_transaction_payment_item/__init__.py rename to erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/__init__.py diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js new file mode 100644 index 0000000000..cf75cc8e41 --- /dev/null +++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js @@ -0,0 +1,14 @@ +frappe.provide('frappe.dashboards.chart_sources'); + +frappe.dashboards.chart_sources["Top 10 Pledged Loan Securities"] = { + method: "erpnext.loan_management.dashboard_chart_source.top_10_pledged_loan_securities.top_10_pledged_loan_securities.get_data", + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company") + } + ] +}; \ No newline at end of file diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json new file mode 100644 index 0000000000..42c9b1c335 --- /dev/null +++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json @@ -0,0 +1,13 @@ +{ + "creation": "2021-02-06 22:01:01.332628", + "docstatus": 0, + "doctype": "Dashboard Chart Source", + "idx": 0, + "modified": "2021-02-06 22:01:01.332628", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Top 10 Pledged Loan Securities", + "owner": "Administrator", + "source_name": "Top 10 Pledged Loan Securities ", + "timeseries": 0 +} \ No newline at end of file diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py new file mode 100644 index 0000000000..6bb04401be --- /dev/null +++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py @@ -0,0 +1,76 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils.dashboard import cache_source +from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applicant_wise_loan_security_exposure \ + import get_loan_security_details +from six import iteritems + +@frappe.whitelist() +@cache_source +def get_data(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None, + to_date = None, timespan = None, time_interval = None, heatmap_year = None): + if chart_name: + chart = frappe.get_doc('Dashboard Chart', chart_name) + else: + chart = frappe._dict(frappe.parse_json(chart)) + + filters = {} + current_pledges = {} + + if filters: + filters = frappe.parse_json(filters)[0] + + conditions = "" + labels = [] + values = [] + + if filters.get('company'): + conditions = "AND company = %(company)s" + + loan_security_details = get_loan_security_details() + + unpledges = frappe._dict(frappe.db.sql(""" + SELECT u.loan_security, sum(u.qty) as qty + FROM `tabLoan Security Unpledge` up, `tabUnpledge` u + WHERE u.parent = up.name + AND up.status = 'Approved' + {conditions} + GROUP BY u.loan_security + """.format(conditions=conditions), filters, as_list=1)) + + pledges = frappe._dict(frappe.db.sql(""" + SELECT p.loan_security, sum(p.qty) as qty + FROM `tabLoan Security Pledge` lp, `tabPledge`p + WHERE p.parent = lp.name + AND lp.status = 'Pledged' + {conditions} + GROUP BY p.loan_security + """.format(conditions=conditions), filters, as_list=1)) + + for security, qty in iteritems(pledges): + current_pledges.setdefault(security, qty) + current_pledges[security] -= unpledges.get(security, 0.0) + + sorted_pledges = dict(sorted(current_pledges.items(), key=lambda item: item[1], reverse=True)) + + count = 0 + for security, qty in iteritems(sorted_pledges): + values.append(qty * loan_security_details.get(security, {}).get('latest_price', 0)) + labels.append(security) + count +=1 + + ## Just need top 10 securities + if count == 10: + break + + return { + 'labels': labels, + 'datasets': [{ + 'name': 'Top 10 Securities', + 'chartType': 'bar', + 'values': values + }] + } \ No newline at end of file diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index e607d4f3cb..83a813f947 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -201,7 +201,9 @@ def request_loan_closure(loan, posting_date=None): write_off_limit = frappe.get_value('Loan Type', loan_type, 'write_off_amount') # checking greater than 0 as there may be some minor precision error - if pending_amount < write_off_limit: + if not pending_amount: + frappe.db.set_value('Loan', loan, 'status', 'Loan Closure Requested') + elif pending_amount < write_off_limit: # Auto create loan write off and update status as loan closure requested write_off = make_loan_write_off(loan) write_off.submit() @@ -348,3 +350,13 @@ def validate_employee_currency_with_company_currency(applicant, 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)) + +@frappe.whitelist() +def get_shortfall_applicants(): + loans = frappe.get_all('Loan Security Shortfall', {'status': 'Pending'}, pluck='loan') + applicants = set(frappe.get_all('Loan', {'name': ('in', loans)}, pluck='name')) + + return { + "value": len(applicants), + "fieldtype": "Int" + } \ No newline at end of file diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index f3c9db6233..13a209418d 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -547,7 +547,7 @@ class TestLoan(unittest.TestCase): # 30 days - grace period penalty_days = 30 - 4 - penalty_applicable_amount = flt(amounts['interest_amount']/2, 2) + penalty_applicable_amount = flt(amounts['interest_amount']/2) penalty_amount = flt((((penalty_applicable_amount * 25) / 100) * penalty_days), 2) process = process_loan_interest_accrual_for_demand_loans(posting_date = '2019-11-30') diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py index e59db4c12d..9c0147e55b 100644 --- a/erpnext/loan_management/doctype/loan_application/loan_application.py +++ b/erpnext/loan_management/doctype/loan_application/loan_application.py @@ -197,7 +197,7 @@ def get_proposed_pledge(securities): security.qty = cint(security.amount/security.loan_security_price) security.amount = security.qty * security.loan_security_price - security.post_haircut_amount = security.amount - (security.amount * security.haircut/100) + security.post_haircut_amount = cint(security.amount - (security.amount * security.haircut/100)) maximum_loan_amount += security.post_haircut_amount diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 7d7992d40a..7978350adf 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -246,7 +246,5 @@ def get_per_day_interest(principal_amount, rate_of_interest, posting_date=None): if not posting_date: posting_date = getdate() - precision = cint(frappe.db.get_default("currency_precision")) or 2 - - return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100), precision) + return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100)) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index ac30c91b67..bac06c4e9e 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -81,8 +81,8 @@ class LoanRepayment(AccountsController): last_accrual_date = get_last_accrual_date(self.against_loan) # get posting date upto which interest has to be accrued - per_day_interest = flt(get_per_day_interest(self.pending_principal_amount, - self.rate_of_interest, self.posting_date), 2) + per_day_interest = get_per_day_interest(self.pending_principal_amount, + self.rate_of_interest, self.posting_date) no_of_days = flt(flt(self.total_interest_paid - self.interest_payable, precision)/per_day_interest, 0) - 1 @@ -105,8 +105,6 @@ class LoanRepayment(AccountsController): }) def update_paid_amount(self): - precision = cint(frappe.db.get_default("currency_precision")) or 2 - loan = frappe.get_doc("Loan", self.against_loan) for payment in self.repayment_details: @@ -114,7 +112,7 @@ class LoanRepayment(AccountsController): SET paid_principal_amount = `paid_principal_amount` + %s, paid_interest_amount = `paid_interest_amount` + %s WHERE name = %s""", - (flt(payment.paid_principal_amount, precision), flt(payment.paid_interest_amount, precision), payment.loan_interest_accrual)) + (flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual)) frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s WHERE name = %s """, (loan.total_amount_paid + self.amount_paid, @@ -148,8 +146,6 @@ class LoanRepayment(AccountsController): frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed") def allocate_amounts(self, repayment_details): - precision = cint(frappe.db.get_default("currency_precision")) or 2 - self.set('repayment_details', []) self.principal_amount_paid = 0 total_interest_paid = 0 @@ -185,21 +181,18 @@ class LoanRepayment(AccountsController): # no of days for which to accrue interest # Interest can only be accrued for an entire day and not partial if interest_paid > repayment_details['unaccrued_interest']: - per_day_interest = flt(get_per_day_interest(self.pending_principal_amount, - self.rate_of_interest, self.posting_date), precision) interest_paid -= repayment_details['unaccrued_interest'] total_interest_paid += repayment_details['unaccrued_interest'] else: # get no of days for which interest can be paid - per_day_interest = flt(get_per_day_interest(self.pending_principal_amount, - self.rate_of_interest, self.posting_date), precision) + per_day_interest = get_per_day_interest(self.pending_principal_amount, + self.rate_of_interest, self.posting_date) no_of_days = cint(interest_paid/per_day_interest) total_interest_paid += no_of_days * per_day_interest interest_paid -= no_of_days * per_day_interest self.total_interest_paid = total_interest_paid - if interest_paid: self.principal_amount_paid += interest_paid @@ -369,7 +362,7 @@ def get_amounts(amounts, against_loan, posting_date): if pending_days > 0: principal_amount = flt(pending_principal_amount, precision) per_day_interest = get_per_day_interest(principal_amount, loan_type_details.rate_of_interest, posting_date) - unaccrued_interest += (pending_days * flt(per_day_interest, precision)) + unaccrued_interest += (pending_days * per_day_interest) amounts["pending_principal_amount"] = flt(pending_principal_amount, precision) amounts["payable_principal_amount"] = flt(payable_principal_amount, precision) diff --git a/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json b/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json new file mode 100644 index 0000000000..e060253d34 --- /dev/null +++ b/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json @@ -0,0 +1,70 @@ +{ + "cards": [ + { + "card": "New Loans" + }, + { + "card": "Active Loans" + }, + { + "card": "Closed Loans" + }, + { + "card": "Total Disbursed" + }, + { + "card": "Open Loan Applications" + }, + { + "card": "New Loan Applications" + }, + { + "card": "Total Sanctioned Amount" + }, + { + "card": "Active Securities" + }, + { + "card": "Applicants With Unpaid Shortfall" + }, + { + "card": "Total Shortfall Amount" + }, + { + "card": "Total Repayment" + }, + { + "card": "Total Write Off" + } + ], + "charts": [ + { + "chart": "New Loans", + "width": "Half" + }, + { + "chart": "Loan Disbursements", + "width": "Half" + }, + { + "chart": "Top 10 Pledged Loan Securities", + "width": "Half" + }, + { + "chart": "Loan Interest Accrual", + "width": "Half" + } + ], + "creation": "2021-02-06 16:52:43.484752", + "dashboard_name": "Loan Dashboard", + "docstatus": 0, + "doctype": "Dashboard", + "idx": 0, + "is_default": 0, + "is_standard": 1, + "modified": "2021-02-21 20:53:47.531699", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Dashboard", + "owner": "Administrator" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/active_loans/active_loans.json b/erpnext/loan_management/number_card/active_loans/active_loans.json new file mode 100644 index 0000000000..7e0db47288 --- /dev/null +++ b/erpnext/loan_management/number_card/active_loans/active_loans.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "", + "creation": "2021-02-06 17:10:26.132493", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"status\",\"in\",[\"Disbursed\",\"Partially Disbursed\",null],false]]", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Active Loans", + "modified": "2021-02-06 17:29:20.304087", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Active Loans", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/active_securities/active_securities.json b/erpnext/loan_management/number_card/active_securities/active_securities.json new file mode 100644 index 0000000000..298e41061a --- /dev/null +++ b/erpnext/loan_management/number_card/active_securities/active_securities.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "", + "creation": "2021-02-06 19:07:21.344199", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Security", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Security\",\"disabled\",\"=\",0,false]]", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Active Securities", + "modified": "2021-02-06 19:07:26.671516", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Active Securities", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/applicants_with_unpaid_shortfall/applicants_with_unpaid_shortfall.json b/erpnext/loan_management/number_card/applicants_with_unpaid_shortfall/applicants_with_unpaid_shortfall.json new file mode 100644 index 0000000000..3b9eba1553 --- /dev/null +++ b/erpnext/loan_management/number_card/applicants_with_unpaid_shortfall/applicants_with_unpaid_shortfall.json @@ -0,0 +1,21 @@ +{ + "creation": "2021-02-07 18:55:12.632616", + "docstatus": 0, + "doctype": "Number Card", + "filters_json": "null", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Applicants With Unpaid Shortfall", + "method": "erpnext.loan_management.doctype.loan.loan.get_shortfall_applicants", + "modified": "2021-02-07 21:46:27.369795", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Applicants With Unpaid Shortfall", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Custom" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/closed_loans/closed_loans.json b/erpnext/loan_management/number_card/closed_loans/closed_loans.json new file mode 100644 index 0000000000..c2f2244265 --- /dev/null +++ b/erpnext/loan_management/number_card/closed_loans/closed_loans.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "", + "creation": "2021-02-21 19:51:49.261813", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"status\",\"=\",\"Closed\",false]]", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Closed Loans", + "modified": "2021-02-21 19:51:54.087903", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Closed Loans", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/last_interest_accrual/last_interest_accrual.json b/erpnext/loan_management/number_card/last_interest_accrual/last_interest_accrual.json new file mode 100644 index 0000000000..65c8ce67d2 --- /dev/null +++ b/erpnext/loan_management/number_card/last_interest_accrual/last_interest_accrual.json @@ -0,0 +1,21 @@ +{ + "creation": "2021-02-07 21:57:14.758007", + "docstatus": 0, + "doctype": "Number Card", + "filters_json": "null", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Last Interest Accrual", + "method": "erpnext.loan_management.doctype.loan.loan.get_last_accrual_date", + "modified": "2021-02-07 21:59:47.525197", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Last Interest Accrual", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Custom" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/new_loan_applications/new_loan_applications.json b/erpnext/loan_management/number_card/new_loan_applications/new_loan_applications.json new file mode 100644 index 0000000000..7e655ff35c --- /dev/null +++ b/erpnext/loan_management/number_card/new_loan_applications/new_loan_applications.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "", + "creation": "2021-02-06 17:59:10.051269", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Application", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Application\",\"docstatus\",\"=\",\"1\",false],[\"Loan Application\",\"creation\",\"Timespan\",\"today\",false]]", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "New Loan Applications", + "modified": "2021-02-06 17:59:21.880979", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "New Loan Applications", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/new_loans/new_loans.json b/erpnext/loan_management/number_card/new_loans/new_loans.json new file mode 100644 index 0000000000..424f0f1495 --- /dev/null +++ b/erpnext/loan_management/number_card/new_loans/new_loans.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "", + "creation": "2021-02-06 17:56:34.624031", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"creation\",\"Timespan\",\"today\",false]]", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "New Loans", + "modified": "2021-02-06 17:58:20.209166", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "New Loans", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/open_loan_applications/open_loan_applications.json b/erpnext/loan_management/number_card/open_loan_applications/open_loan_applications.json new file mode 100644 index 0000000000..1d5e84ed7f --- /dev/null +++ b/erpnext/loan_management/number_card/open_loan_applications/open_loan_applications.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "", + "creation": "2021-02-06 17:23:32.509899", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Application", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Application\",\"docstatus\",\"=\",\"1\",false],[\"Loan Application\",\"status\",\"=\",\"Open\",false]]", + "function": "Count", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Open Loan Applications", + "modified": "2021-02-06 17:29:09.761011", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Open Loan Applications", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/total_disbursed/total_disbursed.json b/erpnext/loan_management/number_card/total_disbursed/total_disbursed.json new file mode 100644 index 0000000000..4a3f8699a0 --- /dev/null +++ b/erpnext/loan_management/number_card/total_disbursed/total_disbursed.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "disbursed_amount", + "creation": "2021-02-06 16:52:19.505462", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Disbursement", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Disbursement\",\"docstatus\",\"=\",\"1\",false]]", + "function": "Sum", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Total Disbursed Amount", + "modified": "2021-02-06 17:29:38.453870", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Total Disbursed", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/total_repayment/total_repayment.json b/erpnext/loan_management/number_card/total_repayment/total_repayment.json new file mode 100644 index 0000000000..38de42b89c --- /dev/null +++ b/erpnext/loan_management/number_card/total_repayment/total_repayment.json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "amount_paid", + "color": "#29CD42", + "creation": "2021-02-21 19:27:45.989222", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Repayment", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Repayment\",\"docstatus\",\"=\",\"1\",false]]", + "function": "Sum", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Total Repayment", + "modified": "2021-02-21 19:34:59.656546", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Total Repayment", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/total_sanctioned_amount/total_sanctioned_amount.json b/erpnext/loan_management/number_card/total_sanctioned_amount/total_sanctioned_amount.json new file mode 100644 index 0000000000..dfb9d24e92 --- /dev/null +++ b/erpnext/loan_management/number_card/total_sanctioned_amount/total_sanctioned_amount.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "loan_amount", + "creation": "2021-02-06 17:05:04.704162", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"status\",\"=\",\"Sanctioned\",false]]", + "function": "Sum", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Total Sanctioned Amount", + "modified": "2021-02-06 17:29:29.930557", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Total Sanctioned Amount", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/total_shortfall_amount/total_shortfall_amount.json b/erpnext/loan_management/number_card/total_shortfall_amount/total_shortfall_amount.json new file mode 100644 index 0000000000..aa6b093732 --- /dev/null +++ b/erpnext/loan_management/number_card/total_shortfall_amount/total_shortfall_amount.json @@ -0,0 +1,23 @@ +{ + "aggregate_function_based_on": "shortfall_amount", + "creation": "2021-02-09 08:07:20.096995", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Security Shortfall", + "dynamic_filters_json": "[]", + "filters_json": "[]", + "function": "Sum", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Total Unpaid Shortfall Amount", + "modified": "2021-02-09 08:09:00.355547", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Total Shortfall Amount", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/number_card/total_write_off/total_write_off.json b/erpnext/loan_management/number_card/total_write_off/total_write_off.json new file mode 100644 index 0000000000..c85169acf8 --- /dev/null +++ b/erpnext/loan_management/number_card/total_write_off/total_write_off.json @@ -0,0 +1,24 @@ +{ + "aggregate_function_based_on": "write_off_amount", + "color": "#CB2929", + "creation": "2021-02-21 19:48:29.004429", + "docstatus": 0, + "doctype": "Number Card", + "document_type": "Loan Write Off", + "dynamic_filters_json": "[]", + "filters_json": "[[\"Loan Write Off\",\"docstatus\",\"=\",\"1\",false]]", + "function": "Sum", + "idx": 0, + "is_public": 0, + "is_standard": 1, + "label": "Total Write Off", + "modified": "2021-02-21 19:48:58.604159", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Total Write Off", + "owner": "Administrator", + "report_function": "Sum", + "show_percentage_stats": 1, + "stats_time_interval": "Daily", + "type": "Document Type" +} \ No newline at end of file diff --git a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py index ab586bc09c..0ccd149e5f 100644 --- a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py +++ b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py @@ -36,7 +36,7 @@ def get_columns(filters): def get_data(filters): data = [] - loan_security_details = get_loan_security_details(filters) + loan_security_details = get_loan_security_details() pledge_values, total_value_map, applicant_type_map = get_applicant_wise_total_loan_security_qty(filters, loan_security_details) @@ -64,7 +64,7 @@ def get_data(filters): return data -def get_loan_security_details(filters): +def get_loan_security_details(): security_detail_map = {} loan_security_price_map = {} lsp_validity_map = {} diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py index a3e69bbfbf..0f72c3cce7 100644 --- a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py +++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py @@ -171,7 +171,7 @@ def get_loan_wise_pledges(filters): return current_pledges def get_loan_wise_security_value(filters, current_pledges): - loan_security_details = get_loan_security_details(filters) + loan_security_details = get_loan_security_details() loan_wise_security_value = {} for key in current_pledges: diff --git a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py index adc8013c68..887a86a46c 100644 --- a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py +++ b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py @@ -35,7 +35,7 @@ def get_columns(filters): def get_data(filters): data = [] - loan_security_details = get_loan_security_details(filters) + loan_security_details = get_loan_security_details() current_pledges, total_portfolio_value = get_company_wise_loan_security_details(filters, loan_security_details) currency = erpnext.get_company_currency(filters.get('company')) @@ -76,7 +76,7 @@ def get_company_wise_loan_security_details(filters, loan_security_details): if qty: security_wise_map[key[1]]['applicant_count'] += 1 - total_portfolio_value += flt(qty * loan_security_details.get(key[1])['latest_price']) + total_portfolio_value += flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0)) return security_wise_map, total_portfolio_value diff --git a/erpnext/loan_management/workspace/loan_management/loan_management.json b/erpnext/loan_management/workspace/loan_management/loan_management.json index 2e8b5bf5b3..18559dceef 100644 --- a/erpnext/loan_management/workspace/loan_management/loan_management.json +++ b/erpnext/loan_management/workspace/loan_management/loan_management.json @@ -10,6 +10,7 @@ "hide_custom": 0, "icon": "loan", "idx": 0, + "is_default": 0, "is_standard": 1, "label": "Loan Management", "links": [ @@ -219,7 +220,7 @@ "type": "Link" } ], - "modified": "2021-01-12 11:27:56.079724", + "modified": "2021-02-18 17:31:53.586508", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Management", @@ -239,6 +240,12 @@ "label": "Loan", "link_to": "Loan", "type": "DocType" + }, + { + "doc_view": "", + "label": "Dashboard", + "link_to": "Loan Dashboard", + "type": "Dashboard" } ] } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index ec28eb7795..662a06b1ee 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -267,6 +267,17 @@ class JobCard(Document): fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"], filters = {"docstatus": 1, "work_order": self.work_order, "operation_id": self.operation_id}) + def set_transferred_qty_in_job_card(self, ste_doc): + for row in ste_doc.items: + if not row.job_card_item: continue + + qty = frappe.db.sql(""" SELECT SUM(qty) from `tabStock Entry Detail` sed, `tabStock Entry` se + WHERE sed.job_card_item = %s and se.docstatus = 1 and sed.parent = se.name and + se.purpose = 'Material Transfer for Manufacture' + """, (row.job_card_item))[0][0] + + frappe.db.set_value('Job Card Item', row.job_card_item, 'transferred_qty', flt(qty)) + def set_transferred_qty(self, update_status=False): if not self.items: self.transferred_qty = self.for_quantity if self.docstatus == 1 else 0 @@ -279,7 +290,8 @@ class JobCard(Document): self.transferred_qty = frappe.db.get_value('Stock Entry', { 'job_card': self.name, 'work_order': self.work_order, - 'docstatus': 1 + 'docstatus': 1, + 'purpose': 'Material Transfer for Manufacture' }, 'sum(fg_completed_qty)') or 0 self.db_set("transferred_qty", self.transferred_qty) @@ -420,6 +432,7 @@ def make_stock_entry(source_name, target_doc=None): target.purpose = "Material Transfer for Manufacture" target.from_bom = 1 target.fg_completed_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0) + target.set_transfer_qty() target.calculate_rate_and_amount() target.set_missing_values() target.set_stock_entry_type() @@ -437,9 +450,10 @@ def make_stock_entry(source_name, target_doc=None): "field_map": { "source_warehouse": "s_warehouse", "required_qty": "qty", - "uom": "stock_uom" + "name": "job_card_item" }, "postprocess": update_item, + "condition": lambda doc: doc.required_qty > 0 } }, target_doc, set_missing_values) diff --git a/erpnext/manufacturing/doctype/job_card_item/job_card_item.json b/erpnext/manufacturing/doctype/job_card_item/job_card_item.json index bc9fe108ca..100ef4ca3a 100644 --- a/erpnext/manufacturing/doctype/job_card_item/job_card_item.json +++ b/erpnext/manufacturing/doctype/job_card_item/job_card_item.json @@ -1,363 +1,120 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-07-09 17:20:44.737289", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2018-07-09 17:20:44.737289", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "source_warehouse", + "uom", + "item_group", + "column_break_3", + "stock_uom", + "item_name", + "description", + "qty_section", + "required_qty", + "column_break_9", + "transferred_qty", + "allow_alternative_item" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "source_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Source Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "source_warehouse", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Source Warehouse", + "options": "Warehouse" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "UOM", - "length": 0, - "no_copy": 0, - "options": "UOM", - "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": "uom", + "fieldtype": "Link", + "label": "UOM", + "options": "UOM" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "description", + "fieldtype": "Text", + "label": "Description", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "qty_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "qty_section", + "fieldtype": "Section Break", + "label": "Qty" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "required_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Required Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "required_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Required Qty", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "allow_alternative_item", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Alternative Item", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "allow_alternative_item", + "fieldtype": "Check", + "label": "Allow Alternative Item" + }, + { + "fetch_from": "item_code.item_group", + "fieldname": "item_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group", + "read_only": 1 + }, + { + "fetch_from": "item_code.stock_uom", + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM" + }, + { + "fieldname": "transferred_qty", + "fieldtype": "Float", + "label": "Transferred Qty", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-08-28 15:23:48.099459", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Job Card Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-02-11 13:50:13.804108", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Job Card Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 06a8e1987d..00e8c5418a 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -94,11 +94,11 @@ class TestWorkOrder(unittest.TestCase): wo_order = make_wo_order_test_record(item="_Test FG Item", qty=2, source_warehouse=warehouse, skip_transfer=1) - bin1_on_submit = get_bin(item, warehouse) + reserved_qty_on_submission = cint(get_bin(item, warehouse).reserved_qty_for_production) # reserved qty for production is updated - self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2, - cint(bin1_on_submit.reserved_qty_for_production)) + self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2, reserved_qty_on_submission) + test_stock_entry.make_stock_entry(item_code="_Test Item", target=warehouse, qty=100, basic_rate=100) @@ -109,9 +109,9 @@ class TestWorkOrder(unittest.TestCase): s.submit() bin1_at_completion = get_bin(item, warehouse) - + self.assertEqual(cint(bin1_at_completion.reserved_qty_for_production), - cint(bin1_on_submit.reserved_qty_for_production) - 1) + reserved_qty_on_submission - 1) def test_production_item(self): wo_order = make_wo_order_test_record(item="_Test FG Item", qty=1, do_not_save=True) diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py index f7b407b792..ffd9242e1b 100644 --- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py +++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py @@ -88,11 +88,11 @@ def get_bom_stock(filters): GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1) def get_manufacturer_records(): - details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no, parent"]) + details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no", "parent"]) manufacture_details = frappe._dict() for detail in details: dic = manufacture_details.setdefault(detail.get('parent'), {}) dic.setdefault('manufacturer', []).append(detail.get('manufacturer')) dic.setdefault('manufacturer_part', []).append(detail.get('manufacturer_part_no')) - return manufacture_details \ No newline at end of file + return manufacture_details diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 3b7c6ab48e..ba31feeefc 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -752,3 +752,7 @@ erpnext.patches.v13_0.set_company_in_leave_ledger_entry erpnext.patches.v13_0.convert_qi_parameter_to_link_field erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021 +erpnext.patches.v12_0.add_state_code_for_ladakh +erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl +erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes +erpnext.patches.v13_0.update_vehicle_no_reqd_condition diff --git a/erpnext/patches/v11_0/refactor_autoname_naming.py b/erpnext/patches/v11_0/refactor_autoname_naming.py index 5dc5d3bf0c..b997ba2db2 100644 --- a/erpnext/patches/v11_0/refactor_autoname_naming.py +++ b/erpnext/patches/v11_0/refactor_autoname_naming.py @@ -20,7 +20,7 @@ doctype_series_map = { 'Certified Consultant': 'NPO-CONS-.YYYY.-.#####', 'Chat Room': 'CHAT-ROOM-.#####', 'Compensatory Leave Request': 'HR-CMP-.YY.-.MM.-.#####', - 'Custom Script': 'SYS-SCR-.#####', + 'Client Script': 'SYS-SCR-.#####', 'Employee Benefit Application': 'HR-BEN-APP-.YY.-.MM.-.#####', 'Employee Benefit Application Detail': '', 'Employee Benefit Claim': 'HR-BEN-CLM-.YY.-.MM.-.#####', diff --git a/erpnext/patches/v11_1/update_bank_transaction_status.py b/erpnext/patches/v11_1/update_bank_transaction_status.py index 1acdfcccf9..544bc5e691 100644 --- a/erpnext/patches/v11_1/update_bank_transaction_status.py +++ b/erpnext/patches/v11_1/update_bank_transaction_status.py @@ -7,9 +7,20 @@ import frappe def execute(): frappe.reload_doc("accounts", "doctype", "bank_transaction") - frappe.db.sql(""" UPDATE `tabBank Transaction` - SET status = 'Reconciled' - WHERE - status = 'Settled' and (debit = allocated_amount or credit = allocated_amount) - and ifnull(allocated_amount, 0) > 0 - """) \ No newline at end of file + bank_transaction_fields = frappe.get_meta("Bank Transaction").get_valid_columns() + + if 'debit' in bank_transaction_fields: + frappe.db.sql(""" UPDATE `tabBank Transaction` + SET status = 'Reconciled' + WHERE + status = 'Settled' and (debit = allocated_amount or credit = allocated_amount) + and ifnull(allocated_amount, 0) > 0 + """) + + elif 'deposit' in bank_transaction_fields: + frappe.db.sql(""" UPDATE `tabBank Transaction` + SET status = 'Reconciled' + WHERE + status = 'Settled' and (deposit = allocated_amount or withdrawal = allocated_amount) + and ifnull(allocated_amount, 0) > 0 + """) \ No newline at end of file diff --git a/erpnext/patches/v12_0/add_state_code_for_ladakh.py b/erpnext/patches/v12_0/add_state_code_for_ladakh.py new file mode 100644 index 0000000000..d41101cc46 --- /dev/null +++ b/erpnext/patches/v12_0/add_state_code_for_ladakh.py @@ -0,0 +1,16 @@ +import frappe +from erpnext.regional.india import states + +def execute(): + + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + custom_fields = ['Address-gst_state', 'Tax Category-gst_state'] + + # Update options in gst_state custom fields + for field in custom_fields: + gst_state_field = frappe.get_doc('Custom Field', field) + gst_state_field.options = '\n'.join(states) + gst_state_field.save() diff --git a/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py b/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py new file mode 100644 index 0000000000..af1f6e7ec1 --- /dev/null +++ b/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py @@ -0,0 +1,26 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe +from frappe.model.utils.rename_field import rename_field + +def execute(): + doctypes = [ + "Bank Statement Settings", + "Bank Statement Settings Item", + "Bank Statement Transaction Entry", + "Bank Statement Transaction Invoice Item", + "Bank Statement Transaction Payment Item", + "Bank Statement Transaction Settings Item", + "Bank Statement Transaction Settings", + ] + + for doctype in doctypes: + frappe.delete_doc("DocType", doctype, force=1) + + frappe.delete_doc("Page", "bank-reconciliation", force=1) + + rename_field("Bank Transaction", "debit", "deposit") + rename_field("Bank Transaction", "credit", "withdrawal") diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py new file mode 100644 index 0000000000..06f7f989bb --- /dev/null +++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py @@ -0,0 +1,46 @@ +import frappe +from frappe import _ +from erpnext.stock.stock_ledger import update_entries_after +from erpnext.accounts.utils import update_gl_entries_after + +def execute(): + frappe.reload_doc('stock', 'doctype', 'repost_item_valuation') + + reposting_project_deployed_on = frappe.db.get_value("DocType", "Repost Item Valuation", "creation") + + data = frappe.db.sql(''' + SELECT + name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time + FROM + `tabStock Ledger Entry` + WHERE + creation > %s + and is_cancelled = 0 + ORDER BY timestamp(posting_date, posting_time) asc, creation asc + ''', reposting_project_deployed_on, as_dict=1) + + frappe.db.auto_commit_on_many_writes = 1 + print("Reposting Stock Ledger Entries...") + total_sle = len(data) + i = 0 + for d in data: + update_entries_after({ + "item_code": d.item_code, + "warehouse": d.warehouse, + "posting_date": d.posting_date, + "posting_time": d.posting_time, + "voucher_type": d.voucher_type, + "voucher_no": d.voucher_no, + "sle_id": d.name + }, allow_negative_stock=True) + + i += 1 + if i%100 == 0: + print(i, "/", total_sle) + + + print("Reposting General Ledger Entries...") + for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}): + update_gl_entries_after('2020-12-25', '01:58:55', company=row.name) + + frappe.db.auto_commit_on_many_writes = 0 diff --git a/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py new file mode 100644 index 0000000000..c26cddbe4e --- /dev/null +++ b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py @@ -0,0 +1,9 @@ +import frappe + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + if frappe.db.exists('Custom Field', { 'fieldname': 'vehicle_no' }): + frappe.db.set_value('Custom Field', { 'fieldname': 'vehicle_no' }, 'mandatory_depends_on', '') diff --git a/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py b/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py index ef3f1d6c0a..c564f8b02a 100644 --- a/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py +++ b/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py @@ -9,7 +9,7 @@ def execute(): # NOTE: sequence is important renamed_fields = get_all_renamed_fields() - for dt, script_field, ref_dt_field in (("Custom Script", "script", "dt"), ("Print Format", "html", "doc_type")): + for dt, script_field, ref_dt_field in (("Client Script", "script", "dt"), ("Print Format", "html", "doc_type")): cond1 = " or ".join("""{0} like "%%{1}%%" """.format(script_field, d[0].replace("_", "\\_")) for d in renamed_fields) cond2 = " and standard = 'No'" if dt == "Print Format" else "" diff --git a/erpnext/patches/v7_0/remove_doctypes_and_reports.py b/erpnext/patches/v7_0/remove_doctypes_and_reports.py index 746cae0e1c..2356e2f6ee 100644 --- a/erpnext/patches/v7_0/remove_doctypes_and_reports.py +++ b/erpnext/patches/v7_0/remove_doctypes_and_reports.py @@ -7,7 +7,7 @@ def execute(): where name in('Time Log Batch', 'Time Log Batch Detail', 'Time Log')""") frappe.db.sql("""delete from `tabDocField` where parent in ('Time Log', 'Time Log Batch')""") - frappe.db.sql("""update `tabCustom Script` set dt = 'Timesheet' where dt = 'Time Log'""") + frappe.db.sql("""update `tabClient Script` set dt = 'Timesheet' where dt = 'Time Log'""") for data in frappe.db.sql(""" select label, fieldname from `tabCustom Field` where dt = 'Time Log'""", as_dict=1): custom_field = frappe.get_doc({ diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 2d3bc57900..60aff02b38 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -1103,10 +1103,10 @@ class SalarySlip(TransactionBase): self.calculate_total_for_salary_slip_based_on_timesheet() else: self.total_deduction = 0.0 - if self.earnings: + if hasattr(self, "earnings"): for earning in self.earnings: self.gross_pay += flt(earning.amount, earning.precision("amount")) - if self.deductions: + if hasattr(self, "deductions"): for deduction in self.deductions: self.total_deduction += flt(deduction.amount, deduction.precision("amount")) self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) - flt(self.total_loan_repayment) diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 7326238273..7a3cb838a9 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -61,5 +61,10 @@ "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" + ], + "js/bank-reconciliation-tool.min.js": [ + "public/js/bank_reconciliation_tool/data_table_manager.js", + "public/js/bank_reconciliation_tool/number_card.js", + "public/js/bank_reconciliation_tool/dialog_manager.js" ] } diff --git a/erpnext/public/images/erpnext-logo.png b/erpnext/public/images/erpnext-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3090727d8ff5f95e49d25278b24b10bf13747f4a GIT binary patch literal 2360 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4Yzkn2Hfk$L90|U1(2s1Lwnj--e zWGoJHcVbv~PUa<$!;&U>cv7h@-A}a#}$5~Go$B+ufw>K9Sh9*k5UVLwOJ(!Va zX^Y;A2EGdl@_l>`i7y#^RP^{9(=r-dj@~ty==pE<-Z0VXbCzc-@3S*_R2nik^f53r zC@?ZGI09YN!~(R06KJv!1A~AH1A~GG1B1haQ3b;&94?q`*kP{E&iDFPT7oB2!_&XD z_5~5k&og@$F%HG3-qv`ucCQqJ+>Ok0zwHd6fknm8%iOovDvb93JGL=j-W!Kx!3@l;vG@68ZvEfg_;k**J@1(rmh2d + "" + + format_currency(value, this.currency) + + "", + }, + { + name: "Withdrawal", + editable: false, + width: 100, + format: (value) => + "" + + format_currency(value, this.currency) + + "", + }, + { + name: "Unallocated Amount", + editable: false, + width: 100, + format: (value) => + "" + + format_currency(value, this.currency) + + "", + }, + { + name: "Reference Number", + editable: false, + width: 140, + }, + { + name: "Actions", + editable: false, + sortable: false, + focusable: false, + dropdown: false, + width: 80, + }, + ]; + } + + format_data(transactions) { + this.transactions = []; + if (transactions[0]) { + this.currency = transactions[0]["currency"]; + } + this.transaction_dt_map = {}; + let length; + transactions.forEach((row) => { + length = this.transactions.push(this.format_row(row)); + this.transaction_dt_map[row["name"]] = length - 1; + }); + } + + format_row(row) { + return [ + row["date"], + row["party_type"], + row["party"], + row["description"], + row["deposit"], + row["withdrawal"], + row["unallocated_amount"], + row["reference_number"], + ` +