From a93b514b2f433eb5f4c0ad3dd28e5f219cbb5ae4 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Tue, 6 Apr 2021 17:10:52 +0530 Subject: [PATCH 001/180] feat: create Quality Inspections from account and stock documents --- erpnext/controllers/accounts_controller.py | 32 +++++ erpnext/public/js/controllers/transaction.js | 133 ++++++++++++++++++- 2 files changed, 161 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 36d399cf19..0c966566fd 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1412,6 +1412,38 @@ def validate_and_delete_children(parent, data): for d in deleted_children: update_bin_on_delete(d, parent.doctype) + +@frappe.whitelist() +def make_quality_inspections(doctype, docname, items): + items = json.loads(items).get('items') + inspections = [] + + for item in items: + if item.get("sample_size") > item.get("qty"): + frappe.throw(_("{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity})").format( + item_name=item.get("item_name"), + sample_size=item.get("sample_size"), + accepted_quantity=item.get("qty") + )) + + quality_inspection = frappe.get_doc({ + "doctype": "Quality Inspection", + "inspection_type": "Incoming", + "inspected_by": frappe.session.user, + "reference_type": doctype, + "reference_name": docname, + "item_code": item.get("item_code"), + "description": item.get("description"), + "sample_size": item.get("sample_size"), + "item_serial_no": item.get("serial_no").split("\n")[0] if item.get("serial_no") else None, + "batch_no": item.get("batch_no") + }).insert() + quality_inspection.save() + inspections.append(quality_inspection) + + return [get_link_to_form("Quality Inspection", inspection.name) for inspection in inspections] + + @frappe.whitelist() def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"): def check_doc_permissions(doc, perm_type='create'): diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 21a20a7bce..da0c87dd85 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -261,11 +261,19 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if(!in_list(["Delivery Note", "Sales Invoice", "Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)) { return; } - var me = this; - var inspection_type = in_list(["Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype) + + const me = this; + if (!this.frm.is_new() && this.frm.doc.docstatus === 0) { + this.frm.add_custom_button(__("Quality Inspection(s)"), () => { + me.make_quality_inspection(); + }, __("Create")); + this.frm.page.set_inner_btn_group_as_primary(__('Create')); + } + + const inspection_type = in_list(["Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype) ? "Incoming" : "Outgoing"; - var quality_inspection_field = this.frm.get_docfield("items", "quality_inspection"); + let quality_inspection_field = this.frm.get_docfield("items", "quality_inspection"); quality_inspection_field.get_route_options_for_new_doc = function(row) { if(me.frm.is_new()) return; return { @@ -280,7 +288,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } this.frm.set_query("quality_inspection", "items", function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; + let d = locals[cdt][cdn]; return { filters: { docstatus: 1, @@ -1909,6 +1917,123 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }); }, + make_quality_inspection: function () { + let data = []; + const fields = [ + { + label: "Items", + fieldtype: "Table", + fieldname: "items", + cannot_add_rows: true, + in_place_edit: true, + data: data, + get_data: () => { return data }, + fields: [ + { + fieldtype: "Data", + fieldname: "docname", + hidden: true + }, + { + fieldtype: "Read Only", + fieldname: "item_code", + label: __("Item Code"), + in_list_view: true + }, + { + fieldtype: "Read Only", + fieldname: "item_name", + label: __("Item Name"), + in_list_view: true + }, + { + fieldtype: "Float", + fieldname: "qty", + label: __("Accepted Quantity"), + in_list_view: true, + read_only: true + }, + { + fieldtype: "Float", + fieldname: "sample_size", + label: __("Sample Size"), + reqd: true, + in_list_view: true + }, + { + fieldtype: "Data", + fieldname: "description", + label: __("Description"), + hidden: true + }, + { + fieldtype: "Data", + fieldname: "serial_no", + label: __("Serial No"), + hidden: true + }, + { + fieldtype: "Data", + fieldname: "batch_no", + label: __("Batch No"), + hidden: true + } + ] + } + ]; + + const me = this; + const dialog = new frappe.ui.Dialog({ + title: __("Select Items for Quality Inspection"), + fields: fields, + primary_action: function () { + const data = dialog.get_values(); + frappe.call({ + method: "erpnext.controllers.accounts_controller.make_quality_inspections", + args: { + doctype: me.frm.doc.doctype, + docname: me.frm.doc.name, + items: data + }, + freeze: true, + callback: function (r) { + if (r.message) { + frappe.msgprint({ + message: __("Quality Inspections Created: {0}", [r.message.join(", ")]), + indicator: "green" + }) + } + dialog.hide(); + } + }); + }, + primary_action_label: __("Create") + }); + + this.frm.doc.items.forEach(item => { + if (!item.quality_inspection) { + let dialog_items = dialog.fields_dict.items; + dialog_items.df.data.push({ + "docname": item.name, + "item_code": item.item_code, + "item_name": item.item_name, + "qty": item.qty, + "description": item.description, + "serial_no": item.serial_no, + "batch_no": item.batch_no + }); + dialog_items.grid.refresh(); + } + }) + + data = dialog.fields_dict.items.df.data; + if (!data.length) { + frappe.msgprint(__("All items in this document already have a linked Quality Inspection.")); + } else { + dialog.show(); + } + }, + get_method_for_payment: function(){ var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry"; if(cur_frm.doc.__onload && cur_frm.doc.__onload.make_payment_via_journal_entry){ From 7f8b95efe8c6c16cf50faf85480b8f97f8eda089 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Wed, 14 Apr 2021 14:12:03 +0530 Subject: [PATCH 002/180] fix: move QI logic to stock module --- erpnext/controllers/accounts_controller.py | 31 ------------ erpnext/controllers/stock_controller.py | 53 ++++++++++++++++---- erpnext/public/js/controllers/transaction.js | 2 +- 3 files changed, 45 insertions(+), 41 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 0c966566fd..ea0b495c10 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1413,37 +1413,6 @@ def validate_and_delete_children(parent, data): update_bin_on_delete(d, parent.doctype) -@frappe.whitelist() -def make_quality_inspections(doctype, docname, items): - items = json.loads(items).get('items') - inspections = [] - - for item in items: - if item.get("sample_size") > item.get("qty"): - frappe.throw(_("{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity})").format( - item_name=item.get("item_name"), - sample_size=item.get("sample_size"), - accepted_quantity=item.get("qty") - )) - - quality_inspection = frappe.get_doc({ - "doctype": "Quality Inspection", - "inspection_type": "Incoming", - "inspected_by": frappe.session.user, - "reference_type": doctype, - "reference_name": docname, - "item_code": item.get("item_code"), - "description": item.get("description"), - "sample_size": item.get("sample_size"), - "item_serial_no": item.get("serial_no").split("\n")[0] if item.get("serial_no") else None, - "batch_no": item.get("batch_no") - }).insert() - quality_inspection.save() - inspections.append(quality_inspection) - - return [get_link_to_form("Quality Inspection", inspection.name) for inspection in inspections] - - @frappe.whitelist() def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"): def check_doc_permissions(doc, perm_type='create'): diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index f352bae30e..7f8da516aa 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -1,17 +1,21 @@ # 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, erpnext -from frappe.utils import cint, flt, cstr, get_link_to_form, today, getdate -from frappe import _ -import frappe.defaults +import json from collections import defaultdict -from erpnext.accounts.utils import get_fiscal_year, check_if_stock_and_account_balance_synced + +import frappe +import frappe.defaults +from frappe import _ +from frappe.utils import cint, cstr, flt, get_link_to_form, getdate + +import erpnext from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map +from erpnext.accounts.utils import check_if_stock_and_account_balance_synced, get_fiscal_year from erpnext.controllers.accounts_controller import AccountsController -from erpnext.stock.stock_ledger import get_valuation_rate from erpnext.stock import get_warehouse_account_map +from erpnext.stock.stock_ledger import get_valuation_rate + class QualityInspectionRequiredError(frappe.ValidationError): pass class QualityInspectionRejectedError(frappe.ValidationError): pass @@ -190,7 +194,6 @@ class StockController(AccountsController): if hasattr(self, "items"): item_doclist = self.get("items") elif self.doctype == "Stock Reconciliation": - import json item_doclist = [] data = json.loads(self.reconciliation_json) for row in data[data.index(self.head_row)+1:]: @@ -320,7 +323,7 @@ class StockController(AccountsController): return serialized_items def validate_warehouse(self): - from erpnext.stock.utils import validate_warehouse_company, validate_disabled_warehouse + from erpnext.stock.utils import validate_disabled_warehouse, validate_warehouse_company warehouses = list(set([d.warehouse for d in self.get("items") if getattr(d, "warehouse", None)])) @@ -501,6 +504,38 @@ class StockController(AccountsController): check_if_stock_and_account_balance_synced(self.posting_date, self.company, self.doctype, self.name) + +@frappe.whitelist() +def make_quality_inspections(doctype, docname, items): + items = json.loads(items).get('items') + inspections = [] + + for item in items: + if item.get("sample_size") > item.get("qty"): + frappe.throw(_("{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity})").format( + item_name=item.get("item_name"), + sample_size=item.get("sample_size"), + accepted_quantity=item.get("qty") + )) + + quality_inspection = frappe.get_doc({ + "doctype": "Quality Inspection", + "inspection_type": "Incoming", + "inspected_by": frappe.session.user, + "reference_type": doctype, + "reference_name": docname, + "item_code": item.get("item_code"), + "description": item.get("description"), + "sample_size": item.get("sample_size"), + "item_serial_no": item.get("serial_no").split("\n")[0] if item.get("serial_no") else None, + "batch_no": item.get("batch_no") + }).insert() + quality_inspection.save() + inspections.append(quality_inspection) + + return [get_link_to_form("Quality Inspection", inspection.name) for inspection in inspections] + + def is_reposting_pending(): return frappe.db.exists("Repost Item Valuation", {'docstatus': 1, 'status': ['in', ['Queued','In Progress']]}) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index da0c87dd85..0e0a87a8b5 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1989,7 +1989,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ primary_action: function () { const data = dialog.get_values(); frappe.call({ - method: "erpnext.controllers.accounts_controller.make_quality_inspections", + method: "erpnext.controllers.stock_controller.make_quality_inspections", args: { doctype: me.frm.doc.doctype, docname: me.frm.doc.name, From 03f711e3a2c6ce12fad80a4f375728a077a8a958 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Wed, 14 Apr 2021 14:41:55 +0530 Subject: [PATCH 003/180] style: sider issues --- erpnext/public/js/controllers/transaction.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 0e0a87a8b5..478b21b441 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1927,7 +1927,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ cannot_add_rows: true, in_place_edit: true, data: data, - get_data: () => { return data }, + get_data: () => { + return data; + }, fields: [ { fieldtype: "Data", @@ -2001,7 +2003,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ frappe.msgprint({ message: __("Quality Inspections Created: {0}", [r.message.join(", ")]), indicator: "green" - }) + }); } dialog.hide(); } @@ -2024,7 +2026,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }); dialog_items.grid.refresh(); } - }) + }); data = dialog.fields_dict.items.df.data; if (!data.length) { From 1e912db3bb43afcff98947e46149c1c848287c0e Mon Sep 17 00:00:00 2001 From: noahjacob Date: Mon, 26 Apr 2021 15:46:18 +0530 Subject: [PATCH 004/180] feat: Added check box to combine items --- .../production_plan/production_plan.js | 18 +- .../production_plan/production_plan.json | 22 +- .../production_plan/production_plan.py | 43 +- .../production_plan_item.json | 950 ++++-------------- .../__init__.py | 0 .../production_plan_item_reference.json | 52 + .../production_plan_item_reference.py | 10 + .../doctype/work_order/work_order.py | 27 +- 8 files changed, 355 insertions(+), 767 deletions(-) create mode 100644 erpnext/manufacturing/doctype/production_plan_item_reference/__init__.py create mode 100644 erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json create mode 100644 erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.py diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 288c1d0cd6..39b8c94bc1 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -211,16 +211,30 @@ frappe.ui.form.on('Production Plan', { }); }, - get_items: function(frm) { + get_items: function (frm) { + frm.clear_table('prod_plan_ref'); + frappe.call({ method: "get_items", freeze: true, doc: frm.doc, - callback: function() { + callback: function () { refresh_field('po_items'); } }); }, + combine_items: function (frm) { + frm.clear_table('po_items'); + frm.clear_table('prod_plan_ref'); + + frappe.call({ + method: "get_items", + freeze: true, + doc: frm.doc, + }); + + + }, get_items_for_mr: function(frm) { if (!frm.doc.for_warehouse) { diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index f11470086a..5c73992d1b 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -28,7 +28,10 @@ "material_requests", "select_items_to_manufacture_section", "get_items", + "combine_items", "po_items", + "section_break_25", + "prod_plan_ref", "material_request_planning", "include_non_stock_items", "include_subcontracted_items", @@ -316,13 +319,30 @@ "fieldname": "include_safety_stock", "fieldtype": "Check", "label": "Include Safety Stock in Required Qty Calculation" + }, + { + "fieldname": "prod_plan_ref", + "fieldtype": "Table", + "hidden": 1, + "label": "Production Plan Item Reference", + "options": "Production Plan Item Reference" + }, + { + "default": "0", + "fieldname": "combine_items", + "fieldtype": "Check", + "label": "Consolidate Items" + }, + { + "fieldname": "section_break_25", + "fieldtype": "Section Break" } ], "icon": "fa fa-calendar", "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-03-08 11:17:25.470147", + "modified": "2021-04-26 14:11:43.564957", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index a3e23a6897..b2bc21fb6d 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -96,8 +96,10 @@ class ProductionPlan(Document): @frappe.whitelist() def get_items(self): + self.set('po_items', []) if self.get_items_from == "Sales Order": - self.get_so_items() + self.get_so_items() + elif self.get_items_from == "Material Request": self.get_mr_items() @@ -165,10 +167,24 @@ class ProductionPlan(Document): self.calculate_total_planned_qty() def add_items(self, items): - self.set('po_items', []) + refs = {} for data in items: item_details = get_item_details(data.item_code) + if self.combine_items: + if item_details.bom_no in refs.keys(): + refs[item_details.bom_no]['qty'] = refs[item_details.bom_no]['qty'] + data.pending_qty + refs[item_details.bom_no]['so'].append(data.parent) + refs[item_details.bom_no]['so_items'].append(data.name) + refs[item_details.bom_no]['planned_qty'].append(data.pending_qty) + continue + else: + refs[item_details.bom_no] = {'qty': data.pending_qty, 'ref': data.name} + refs[item_details.bom_no]['so'] = [data.parent] + refs[item_details.bom_no]['so_items'] = [data.name] + refs[item_details.bom_no]['planned_qty'] = [data.pending_qty] + pi = self.append('po_items', { + 'name': data.name, 'include_exploded_items': 1, 'warehouse': data.warehouse, 'item_code': data.item_code, @@ -185,11 +201,32 @@ class ProductionPlan(Document): pi.sales_order = data.parent pi.sales_order_item = data.name pi.description = data.description - + + elif self.get_items_from == "Material Request": pi.material_request = data.parent pi.material_request_item = data.name pi.description = data.description + + if refs: + for d in self.po_items: + d.planned_qty = refs[d.bom_no]['qty'] + d.pending_qty = refs[d.bom_no]['qty'] + d.sales_order = '' + self.add_pp_ref(refs) + + def add_pp_ref(self, refs): + for r in refs: + idx = 0 + for so in refs[r]['so']: + self.append('prod_plan_ref', { + 'item_ref': refs[r]['ref'], + 'sales_order': so, + 'sales_order_item':refs[r]['so_items'][idx], + 'qty':refs[r]['planned_qty'][idx] + }) + idx+=1 + def calculate_total_planned_qty(self): self.total_planned_qty = 0 diff --git a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json index d0dce53437..9ff1717e70 100644 --- a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json +++ b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json @@ -1,792 +1,222 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "hash", - "beta": 0, - "creation": "2013-02-22 01:27:49", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, + "actions": [], + "autoname": "hash", + "creation": "2013-02-22 01:27:49", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "include_exploded_items", + "item_code", + "bom_no", + "planned_qty", + "column_break_6", + "make_work_order_for_sub_assembly_items", + "warehouse", + "planned_start_date", + "section_break_9", + "pending_qty", + "ordered_qty", + "produced_qty", + "column_break_17", + "description", + "stock_uom", + "reference_section", + "sales_order", + "sales_order_item", + "column_break_19", + "material_request", + "material_request_item", + "product_bundle_item" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fetch_if_empty": 0, - "fieldname": "include_exploded_items", - "fieldtype": "Check", - "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": "Include Exploded Items", - "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 - }, + "columns": 2, + "default": "0", + "fieldname": "include_exploded_items", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Include Exploded Items" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fetch_if_empty": 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, - "oldfieldname": "item_code", - "oldfieldtype": "Link", - "options": "Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "columns": 2, + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "oldfieldname": "item_code", + "oldfieldtype": "Link", + "options": "Item", + "print_width": "150px", + "reqd": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fetch_if_empty": 0, - "fieldname": "bom_no", - "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": "BOM No", - "length": 0, - "no_copy": 0, - "oldfieldname": "bom_no", - "oldfieldtype": "Link", - "options": "BOM", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "columns": 2, + "fieldname": "bom_no", + "fieldtype": "Link", + "in_list_view": 1, + "label": "BOM No", + "oldfieldname": "bom_no", + "oldfieldtype": "Link", + "options": "BOM", + "print_width": "100px", + "reqd": 1, "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "planned_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": "Planned Qty", - "length": 0, - "no_copy": 0, - "oldfieldname": "planned_qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "100px", - "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": "planned_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Planned Qty", + "oldfieldname": "planned_qty", + "oldfieldtype": "Currency", + "print_width": "100px", + "reqd": 1, "width": "100px" - }, + }, { - "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_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "If enabled, system will create the work order for the exploded items against which BOM is available.", - "fetch_if_empty": 0, - "fieldname": "make_work_order_for_sub_assembly_items", - "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": "Make Work Order for Sub Assembly Items", - "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", + "description": "If enabled, system will create the work order for the exploded items against which BOM is available.", + "fieldname": "make_work_order_for_sub_assembly_items", + "fieldtype": "Check", + "label": "Make Work Order for Sub Assembly Items" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, - "fieldname": "warehouse", - "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": "For Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "print_hide": 0, - "print_hide_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": "warehouse", + "fieldtype": "Link", + "in_list_view": 1, + "label": "For Warehouse", + "options": "Warehouse" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fetch_if_empty": 0, - "fieldname": "planned_start_date", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Planned Start Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "planned_start_date", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Planned Start Date", + "reqd": 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_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, - "label": "Quantity and 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 - }, + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "label": "Quantity and Description" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "pending_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Pending Qty", - "length": 0, - "no_copy": 0, - "oldfieldname": "prevdoc_reqd_qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "100px", - "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": "pending_qty", + "fieldtype": "Float", + "label": "Pending Qty", + "oldfieldname": "prevdoc_reqd_qty", + "oldfieldtype": "Currency", + "print_width": "100px", + "read_only": 1, "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "ordered_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Ordered Qty", - "length": 0, - "no_copy": 0, - "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": "ordered_qty", + "fieldtype": "Float", + "label": "Ordered Qty", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "produced_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Produced Qty", - "length": 0, - "no_copy": 1, - "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 - }, + "default": "0", + "fieldname": "produced_qty", + "fieldtype": "Float", + "label": "Produced Qty", + "no_copy": 1, + "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 - }, + "fieldname": "column_break_17", + "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": "description", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "description", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "200px", - "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 Editor", + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "print_width": "200px", + "read_only": 1, "width": "200px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "stock_uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "UOM", - "length": 0, - "no_copy": 0, - "oldfieldname": "stock_uom", - "oldfieldtype": "Data", - "options": "UOM", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "80px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "UOM", + "oldfieldname": "stock_uom", + "oldfieldtype": "Data", + "options": "UOM", + "print_width": "80px", + "read_only": 1, + "reqd": 1, "width": "80px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "reference_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": "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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_section", + "fieldtype": "Section Break", + "label": "Reference" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "sales_order", - "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": "Sales Order", - "length": 0, - "no_copy": 0, - "oldfieldname": "source_docname", - "oldfieldtype": "Data", - "options": "Sales Order", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sales_order", + "fieldtype": "Link", + "label": "Sales Order", + "oldfieldname": "source_docname", + "oldfieldtype": "Data", + "options": "Sales Order", + "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": "sales_order_item", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Order Item", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sales_order_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Sales Order Item", + "no_copy": 1, + "print_hide": 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_19", - "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_19", + "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": "material_request", - "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": "Material Request", - "length": 0, - "no_copy": 0, - "options": "Material Request", - "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": "material_request", + "fieldtype": "Link", + "label": "Material Request", + "options": "Material Request", + "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": "material_request_item", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "material_request_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 - }, + "fieldname": "material_request_item", + "fieldtype": "Data", + "hidden": 1, + "label": "material_request_item" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "product_bundle_item", - "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": "Product Bundle Item", - "length": 0, - "no_copy": 1, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "product_bundle_item", + "fieldtype": "Link", + "label": "Product Bundle Item", + "no_copy": 1, + "options": "Item", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_toolbar": 0, - "idx": 1, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-04-08 23:09:57.199423", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Production Plan Item", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "show_name_in_global_search": 0, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "idx": 1, + "istable": 1, + "links": [], + "modified": "2021-04-22 12:10:01.102440", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Production Plan Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "ASC" } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/production_plan_item_reference/__init__.py b/erpnext/manufacturing/doctype/production_plan_item_reference/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json new file mode 100644 index 0000000000..19e813c5b3 --- /dev/null +++ b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json @@ -0,0 +1,52 @@ +{ + "actions": [], + "creation": "2021-04-22 10:32:58.896330", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_ref", + "sales_order", + "sales_order_item", + "qty" + ], + "fields": [ + { + "fieldname": "item_ref", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Reference" + }, + { + "fieldname": "sales_order", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Sales Order Reference", + "options": "Sales Order" + }, + { + "fieldname": "sales_order_item", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Sales Order Item" + }, + { + "fieldname": "qty", + "fieldtype": "Data", + "in_list_view": 1, + "label": "qty" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-04-23 16:55:22.161418", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Production Plan Item Reference", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.py b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.py new file mode 100644 index 0000000000..51fbc3633b --- /dev/null +++ b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.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 ProductionPlanItemReference(Document): + pass diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 8507f5eb34..bd286507ec 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -241,7 +241,13 @@ class WorkOrder(Document): if not self.fg_warehouse: frappe.throw(_("For Warehouse is required before Submit")) - self.update_work_order_qty_in_so() + prod_plan = frappe.get_doc('Production Plan', self.production_plan) + pp_ref = prod_plan.prod_plan_ref + if pp_ref: + self.update_work_order_qty_in_combined_so() + else: + self.update_work_order_qty_in_so() + self.update_reserved_qty_for_production() self.update_completed_qty_in_material_request() self.update_planned_qty() @@ -357,6 +363,25 @@ class WorkOrder(Document): work_order_qty = qty[0][0] if qty and qty[0][0] else 0 frappe.db.set_value('Sales Order Item', self.sales_order_item, 'work_order_qty', flt(work_order_qty/total_bundle_qty, 2)) + + def update_work_order_qty_in_combined_so(self): + total_bundle_qty = 1 + if self.product_bundle_item: + total_bundle_qty = frappe.db.sql(""" select sum(qty) from + `tabProduct Bundle Item` where parent = %s""", (frappe.db.escape(self.product_bundle_item)))[0][0] + + if not total_bundle_qty: + # product bundle is 0 (product bundle allows 0 qty for items) + total_bundle_qty = 1 + + prod_plan = frappe.get_doc('Production Plan', self.production_plan) + pp_ref = prod_plan.prod_plan_ref + for p in pp_ref: + if p.item_ref == self.production_plan_item: + work_order_qty = int(p.qty) + frappe.db.set_value('Sales Order Item', + p.sales_order_item, 'work_order_qty', flt(work_order_qty/total_bundle_qty, 2)) + def update_completed_qty_in_material_request(self): if self.material_request: From 82905166d988f162b6fa84a49ee63a0704bda661 Mon Sep 17 00:00:00 2001 From: noahjacob Date: Wed, 28 Apr 2021 10:12:17 +0530 Subject: [PATCH 005/180] fix: Fixed updating sales order work qty after cancelling work order --- .../doctype/production_plan/production_plan.js | 1 - .../manufacturing/doctype/work_order/work_order.py | 12 +++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 39b8c94bc1..29c3d5b18e 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -224,7 +224,6 @@ frappe.ui.form.on('Production Plan', { }); }, combine_items: function (frm) { - frm.clear_table('po_items'); frm.clear_table('prod_plan_ref'); frappe.call({ diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index bd286507ec..87d57ad42c 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -258,7 +258,13 @@ class WorkOrder(Document): self.validate_cancel() frappe.db.set(self,'status', 'Cancelled') - self.update_work_order_qty_in_so() + prod_plan = frappe.get_doc('Production Plan', self.production_plan) + pp_ref = prod_plan.prod_plan_ref + if pp_ref: + self.update_work_order_qty_in_combined_so(cancel = True) + else: + self.update_work_order_qty_in_so() + self.delete_job_card() self.update_completed_qty_in_material_request() self.update_planned_qty() @@ -364,7 +370,7 @@ class WorkOrder(Document): frappe.db.set_value('Sales Order Item', self.sales_order_item, 'work_order_qty', flt(work_order_qty/total_bundle_qty, 2)) - def update_work_order_qty_in_combined_so(self): + def update_work_order_qty_in_combined_so(self, cancel = None): total_bundle_qty = 1 if self.product_bundle_item: total_bundle_qty = frappe.db.sql(""" select sum(qty) from @@ -378,7 +384,7 @@ class WorkOrder(Document): pp_ref = prod_plan.prod_plan_ref for p in pp_ref: if p.item_ref == self.production_plan_item: - work_order_qty = int(p.qty) + work_order_qty = int(p.qty) if not cancel else 0 frappe.db.set_value('Sales Order Item', p.sales_order_item, 'work_order_qty', flt(work_order_qty/total_bundle_qty, 2)) From b7ca9139042784530157d104126f3569bcd1d1d8 Mon Sep 17 00:00:00 2001 From: noahjacob Date: Wed, 28 Apr 2021 19:21:36 +0530 Subject: [PATCH 006/180] fix: Added Item Reference field to link tables and update work_order_qty --- .../doctype/production_plan/production_plan.py | 1 + .../production_plan_item/production_plan_item.json | 11 +++++++++-- .../manufacturing/doctype/work_order/work_order.py | 4 +++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index b2bc21fb6d..088089f87f 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -201,6 +201,7 @@ class ProductionPlan(Document): pi.sales_order = data.parent pi.sales_order_item = data.name pi.description = data.description + pi.item_reference = data.name elif self.get_items_from == "Material Request": diff --git a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json index 9ff1717e70..89ab7aa0a0 100644 --- a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json +++ b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json @@ -27,7 +27,8 @@ "column_break_19", "material_request", "material_request_item", - "product_bundle_item" + "product_bundle_item", + "item_reference" ], "fields": [ { @@ -206,12 +207,18 @@ "options": "Item", "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "item_reference", + "fieldtype": "Data", + "hidden": 1, + "label": "Item Reference" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-04-22 12:10:01.102440", + "modified": "2021-04-28 19:14:57.772123", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan Item", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 87d57ad42c..d77c46fb03 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -382,8 +382,10 @@ class WorkOrder(Document): prod_plan = frappe.get_doc('Production Plan', self.production_plan) pp_ref = prod_plan.prod_plan_ref + pp_item = frappe.get_doc('Production Plan Item', self.production_plan_item) + item_ref = pp_item.item_reference for p in pp_ref: - if p.item_ref == self.production_plan_item: + if p.item_ref == item_ref: work_order_qty = int(p.qty) if not cancel else 0 frappe.db.set_value('Sales Order Item', p.sales_order_item, 'work_order_qty', flt(work_order_qty/total_bundle_qty, 2)) From 56f697052cc01f3beab8a12d5b9978e2bf86ebbe Mon Sep 17 00:00:00 2001 From: noahjacob Date: Wed, 28 Apr 2021 19:25:22 +0530 Subject: [PATCH 007/180] test: added test case for combining items --- .../production_plan/test_production_plan.py | 57 ++++++++++++++++++- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 27335aa204..2f52ad9021 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -100,7 +100,7 @@ class TestProductionPlan(unittest.TestCase): def test_production_plan_sales_orders(self): item = 'Test Production Item 1' - so = make_sales_order(item_code=item, qty=5) + so = make_sales_order(item_code=item, qty=1) sales_order = so.name sales_order_item = so.items[0].name @@ -124,8 +124,8 @@ class TestProductionPlan(unittest.TestCase): wo_doc = frappe.get_doc('Work Order', work_order) wo_doc.update({ - 'wip_warehouse': '_Test Warehouse 1 - _TC', - 'fg_warehouse': '_Test Warehouse - _TC' + 'wip_warehouse': 'Work In Progress - _TC', + 'fg_warehouse': 'Finished Goods - _TC' }) wo_doc.submit() @@ -145,6 +145,57 @@ class TestProductionPlan(unittest.TestCase): self.assertEqual(sales_orders, []) + def test_production_plan_combine_items(self): + item = 'Test Production Item 1' + so = make_sales_order(item_code=item, qty=1) + sales_order = so.name + sales_order_item = so.items[0].name + + pln = frappe.new_doc('Production Plan') + pln.company = so.company + pln.get_items_from = 'Sales Order' + pln.append('sales_orders', { + 'sales_order': so.name, + 'sales_order_date': so.transaction_date, + 'customer': so.customer, + 'grand_total': so.grand_total + }) + so = make_sales_order(item_code=item, qty=2) + pln.append('sales_orders', { + 'sales_order': so.name, + 'sales_order_date': so.transaction_date, + 'customer': so.customer, + 'grand_total': so.grand_total + }) + pln.combine_items = 1 + pln.get_so_items() + pln.save() + pp = frappe.get_doc('Production Plan',pln.name) + for d in pp.prod_plan_ref: + d.item_ref = pp.po_items[0].name + pln.submit() + + self.assertTrue(pln.po_items[0].planned_qty,3) + + + pln.make_work_order() + + work_order = frappe.db.get_value('Work Order', {'production_plan_item': pln.po_items[0].name, + 'production_plan': pln.name,}, 'name') + + wo_doc = frappe.get_doc('Work Order', work_order) + wo_doc.update({ + 'wip_warehouse': 'Work In Progress - _TC', + }) + + wo_doc.submit() + for d in pln.prod_plan_ref: + so_wo_qty = frappe.db.get_value('Sales Order Item', d.sales_order_item, 'work_order_qty') + self.assertTrue(so_wo_qty,d.qty) + + + + def test_pp_to_mr_customer_provided(self): #Material Request from Production Plan for Customer Provided create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0) From 93c22ebbb98324153b6644475478c62ee15f8ff8 Mon Sep 17 00:00:00 2001 From: noahjacob Date: Thu, 29 Apr 2021 12:57:41 +0530 Subject: [PATCH 008/180] refactor: created separate function to update work_order on cancel --- .../doctype/work_order/work_order.py | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index d77c46fb03..d9956e5bca 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -242,8 +242,8 @@ class WorkOrder(Document): frappe.throw(_("For Warehouse is required before Submit")) prod_plan = frappe.get_doc('Production Plan', self.production_plan) - pp_ref = prod_plan.prod_plan_ref - if pp_ref: + + if prod_plan.prod_plan_ref: self.update_work_order_qty_in_combined_so() else: self.update_work_order_qty_in_so() @@ -259,9 +259,9 @@ class WorkOrder(Document): frappe.db.set(self,'status', 'Cancelled') prod_plan = frappe.get_doc('Production Plan', self.production_plan) - pp_ref = prod_plan.prod_plan_ref - if pp_ref: - self.update_work_order_qty_in_combined_so(cancel = True) + + if prod_plan.prod_plan_ref: + self.update_work_order_combined_on_cancel() else: self.update_work_order_qty_in_so() @@ -370,7 +370,7 @@ class WorkOrder(Document): frappe.db.set_value('Sales Order Item', self.sales_order_item, 'work_order_qty', flt(work_order_qty/total_bundle_qty, 2)) - def update_work_order_qty_in_combined_so(self, cancel = None): + def update_work_order_qty_in_combined_so(self): total_bundle_qty = 1 if self.product_bundle_item: total_bundle_qty = frappe.db.sql(""" select sum(qty) from @@ -381,14 +381,22 @@ class WorkOrder(Document): total_bundle_qty = 1 prod_plan = frappe.get_doc('Production Plan', self.production_plan) - pp_ref = prod_plan.prod_plan_ref pp_item = frappe.get_doc('Production Plan Item', self.production_plan_item) - item_ref = pp_item.item_reference - for p in pp_ref: - if p.item_ref == item_ref: - work_order_qty = int(p.qty) if not cancel else 0 + + for p in prod_plan.prod_plan_ref: + if p.item_ref == pp_item.item_reference: + work_order_qty = int(p.qty) frappe.db.set_value('Sales Order Item', p.sales_order_item, 'work_order_qty', flt(work_order_qty/total_bundle_qty, 2)) + + def update_work_order_combined_on_cancel(self): + prod_plan = frappe.get_doc('Production Plan', self.production_plan) + pp_item = frappe.get_doc('Production Plan Item', self.production_plan_item) + + for p in prod_plan.prod_plan_ref: + if p.item_ref == pp_item.item_reference: + frappe.db.set_value('Sales Order Item', + p.sales_order_item, 'work_order_qty', 0.0) def update_completed_qty_in_material_request(self): From 90c667205adce829d9e0f9c4d74c4dd9effaa065 Mon Sep 17 00:00:00 2001 From: noahjacob Date: Thu, 29 Apr 2021 12:58:35 +0530 Subject: [PATCH 009/180] test: added on_cancel test --- .../production_plan/test_production_plan.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 2f52ad9021..19b06bc8dd 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -148,8 +148,6 @@ class TestProductionPlan(unittest.TestCase): def test_production_plan_combine_items(self): item = 'Test Production Item 1' so = make_sales_order(item_code=item, qty=1) - sales_order = so.name - sales_order_item = so.items[0].name pln = frappe.new_doc('Production Plan') pln.company = so.company @@ -169,17 +167,13 @@ class TestProductionPlan(unittest.TestCase): }) pln.combine_items = 1 pln.get_so_items() - pln.save() - pp = frappe.get_doc('Production Plan',pln.name) - for d in pp.prod_plan_ref: - d.item_ref = pp.po_items[0].name + for d in pln.prod_plan_ref: + d.item_ref = pln.po_items[0].name pln.submit() - self.assertTrue(pln.po_items[0].planned_qty,3) - - - pln.make_work_order() + self.assertTrue(pln.po_items[0].planned_qty,3) + pln.make_work_order() work_order = frappe.db.get_value('Work Order', {'production_plan_item': pln.po_items[0].name, 'production_plan': pln.name,}, 'name') @@ -189,13 +183,19 @@ class TestProductionPlan(unittest.TestCase): }) wo_doc.submit() + so_items = [] for d in pln.prod_plan_ref: + so_items.append(d.sales_order_item) so_wo_qty = frappe.db.get_value('Sales Order Item', d.sales_order_item, 'work_order_qty') - self.assertTrue(so_wo_qty,d.qty) - - - - + self.assertEqual(so_wo_qty, d.qty) + wo_doc.cancel() + for s in so_items: + so_wo_qty = frappe.db.get_value('Sales Order Item', s, 'work_order_qty') + self.assertEqual(so_wo_qty, 0.0) + + lat_plan = frappe.get_doc('Production Plan',pln.name) + lat_plan.cancel() + def test_pp_to_mr_customer_provided(self): #Material Request from Production Plan for Customer Provided create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0) From 50f52dfbcbf3124c1f9468807f1f22e6df2cb10c Mon Sep 17 00:00:00 2001 From: noahjacob Date: Fri, 7 May 2021 17:18:48 +0530 Subject: [PATCH 010/180] refactor: variable names and refactored cancel function into submit function --- .../production_plan/production_plan.json | 18 +++++------ .../production_plan/production_plan.py | 16 +++++----- .../production_plan/test_production_plan.py | 17 +++++----- .../production_plan_item_reference.json | 16 +++++----- .../doctype/work_order/work_order.py | 32 ++++++------------- 5 files changed, 44 insertions(+), 55 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 5c73992d1b..3041507caf 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -31,7 +31,7 @@ "combine_items", "po_items", "section_break_25", - "prod_plan_ref", + "prod_plan_references", "material_request_planning", "include_non_stock_items", "include_subcontracted_items", @@ -320,13 +320,6 @@ "fieldtype": "Check", "label": "Include Safety Stock in Required Qty Calculation" }, - { - "fieldname": "prod_plan_ref", - "fieldtype": "Table", - "hidden": 1, - "label": "Production Plan Item Reference", - "options": "Production Plan Item Reference" - }, { "default": "0", "fieldname": "combine_items", @@ -336,13 +329,20 @@ { "fieldname": "section_break_25", "fieldtype": "Section Break" + }, + { + "fieldname": "prod_plan_references", + "fieldtype": "Table", + "hidden": 1, + "label": "Production Plan Item Reference", + "options": "Production Plan Item Reference" } ], "icon": "fa fa-calendar", "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-04-26 14:11:43.564957", + "modified": "2021-05-07 16:56:00.255001", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 088089f87f..8d578fd935 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -172,7 +172,7 @@ class ProductionPlan(Document): item_details = get_item_details(data.item_code) if self.combine_items: if item_details.bom_no in refs.keys(): - refs[item_details.bom_no]['qty'] = refs[item_details.bom_no]['qty'] + data.pending_qty + refs[item_details.bom_no]['qty'] += data.pending_qty refs[item_details.bom_no]['so'].append(data.parent) refs[item_details.bom_no]['so_items'].append(data.name) refs[item_details.bom_no]['planned_qty'].append(data.pending_qty) @@ -217,15 +217,15 @@ class ProductionPlan(Document): self.add_pp_ref(refs) def add_pp_ref(self, refs): - for r in refs: + for bom_no in refs: idx = 0 - for so in refs[r]['so']: - self.append('prod_plan_ref', { - 'item_ref': refs[r]['ref'], + for so in refs[bom_no]['so']: + self.append('prod_plan_references', { + 'item_reference': refs[bom_no]['ref'], 'sales_order': so, - 'sales_order_item':refs[r]['so_items'][idx], - 'qty':refs[r]['planned_qty'][idx] - }) + 'sales_order_item':refs[bom_no]['so_items'][idx], + 'qty':refs[bom_no]['planned_qty'][idx] + }) idx+=1 diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 19b06bc8dd..ec5c5e0e13 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -167,8 +167,8 @@ class TestProductionPlan(unittest.TestCase): }) pln.combine_items = 1 pln.get_so_items() - for d in pln.prod_plan_ref: - d.item_ref = pln.po_items[0].name + for plan_reference in pln.prod_plan_references: + plan_reference.item_reference = pln.po_items[0].name pln.submit() self.assertTrue(pln.po_items[0].planned_qty,3) @@ -184,13 +184,14 @@ class TestProductionPlan(unittest.TestCase): wo_doc.submit() so_items = [] - for d in pln.prod_plan_ref: - so_items.append(d.sales_order_item) - so_wo_qty = frappe.db.get_value('Sales Order Item', d.sales_order_item, 'work_order_qty') - self.assertEqual(so_wo_qty, d.qty) + for plan_reference in pln.prod_plan_references: + so_items.append(plan_reference.sales_order_item) + so_wo_qty = frappe.db.get_value('Sales Order Item', plan_reference.sales_order_item, 'work_order_qty') + self.assertEqual(so_wo_qty, plan_reference.qty) + wo_doc.cancel() - for s in so_items: - so_wo_qty = frappe.db.get_value('Sales Order Item', s, 'work_order_qty') + for so_item in so_items: + so_wo_qty = frappe.db.get_value('Sales Order Item', so_item, 'work_order_qty') self.assertEqual(so_wo_qty, 0.0) lat_plan = frappe.get_doc('Production Plan',pln.name) diff --git a/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json index 19e813c5b3..84dee4ad28 100644 --- a/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json +++ b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json @@ -5,18 +5,12 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "item_ref", + "item_reference", "sales_order", "sales_order_item", "qty" ], "fields": [ - { - "fieldname": "item_ref", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Item Reference" - }, { "fieldname": "sales_order", "fieldtype": "Link", @@ -35,12 +29,18 @@ "fieldtype": "Data", "in_list_view": 1, "label": "qty" + }, + { + "fieldname": "item_reference", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Reference" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-04-23 16:55:22.161418", + "modified": "2021-05-07 17:03:49.707487", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan Item Reference", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index d9956e5bca..bb6450b775 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -240,10 +240,8 @@ class WorkOrder(Document): frappe.throw(_("Work-in-Progress Warehouse is required before Submit")) if not self.fg_warehouse: frappe.throw(_("For Warehouse is required before Submit")) - - prod_plan = frappe.get_doc('Production Plan', self.production_plan) - if prod_plan.prod_plan_ref: + if self.production_plan and frappe.db.exists('Production Plan Item Reference',{'parent':self.production_plan}): self.update_work_order_qty_in_combined_so() else: self.update_work_order_qty_in_so() @@ -256,12 +254,10 @@ class WorkOrder(Document): def on_cancel(self): self.validate_cancel() - frappe.db.set(self,'status', 'Cancelled') - prod_plan = frappe.get_doc('Production Plan', self.production_plan) - if prod_plan.prod_plan_ref: - self.update_work_order_combined_on_cancel() + if self.production_plan and frappe.db.exists('Production Plan Item Reference',{'parent':self.production_plan}): + self.update_work_order_qty_in_combined_so() else: self.update_work_order_qty_in_so() @@ -381,24 +377,16 @@ class WorkOrder(Document): total_bundle_qty = 1 prod_plan = frappe.get_doc('Production Plan', self.production_plan) - pp_item = frappe.get_doc('Production Plan Item', self.production_plan_item) + item_reference = frappe.get_value('Production Plan Item', self.production_plan_item,'item_reference') - for p in prod_plan.prod_plan_ref: - if p.item_ref == pp_item.item_reference: - work_order_qty = int(p.qty) + for plan_reference in prod_plan.prod_plan_references: + work_order_qty = 0.0 + if plan_reference.item_reference == item_reference: + if self.docstatus == 1: + work_order_qty = cint(plan_reference.qty) / total_bundle_qty frappe.db.set_value('Sales Order Item', - p.sales_order_item, 'work_order_qty', flt(work_order_qty/total_bundle_qty, 2)) + plan_reference.sales_order_item, 'work_order_qty', work_order_qty) - def update_work_order_combined_on_cancel(self): - prod_plan = frappe.get_doc('Production Plan', self.production_plan) - pp_item = frappe.get_doc('Production Plan Item', self.production_plan_item) - - for p in prod_plan.prod_plan_ref: - if p.item_ref == pp_item.item_reference: - frappe.db.set_value('Sales Order Item', - p.sales_order_item, 'work_order_qty', 0.0) - - def update_completed_qty_in_material_request(self): if self.material_request: frappe.get_doc("Material Request", self.material_request).update_completed_qty([self.material_request_item]) From 516c789127d8514368fc35392368b197534d5e0b Mon Sep 17 00:00:00 2001 From: noahjacob Date: Wed, 19 May 2021 13:29:30 +0530 Subject: [PATCH 011/180] refactor: variable names and suggested changes --- .../production_plan/production_plan.js | 6 +-- .../production_plan/production_plan.py | 51 ++++++++++--------- .../production_plan/test_production_plan.py | 16 +++--- .../doctype/work_order/work_order.py | 2 +- 4 files changed, 38 insertions(+), 37 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 29c3d5b18e..64d584118f 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -212,7 +212,7 @@ frappe.ui.form.on('Production Plan', { }, get_items: function (frm) { - frm.clear_table('prod_plan_ref'); + frm.clear_table('prod_plan_references'); frappe.call({ method: "get_items", @@ -224,15 +224,13 @@ frappe.ui.form.on('Production Plan', { }); }, combine_items: function (frm) { - frm.clear_table('prod_plan_ref'); + frm.clear_table('prod_plan_references'); frappe.call({ method: "get_items", freeze: true, doc: frm.doc, }); - - }, get_items_for_mr: function(frm) { diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 8d578fd935..46e047654b 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -171,20 +171,28 @@ class ProductionPlan(Document): for data in items: item_details = get_item_details(data.item_code) if self.combine_items: - if item_details.bom_no in refs.keys(): + if item_details.bom_no in refs: + refs[item_details.bom_no]['so_details'].append({ + 'sales_order': data.parent, + 'sales_order_item': data.name, + 'qty': data.pending_qty + }) refs[item_details.bom_no]['qty'] += data.pending_qty - refs[item_details.bom_no]['so'].append(data.parent) - refs[item_details.bom_no]['so_items'].append(data.name) - refs[item_details.bom_no]['planned_qty'].append(data.pending_qty) continue + else: - refs[item_details.bom_no] = {'qty': data.pending_qty, 'ref': data.name} - refs[item_details.bom_no]['so'] = [data.parent] - refs[item_details.bom_no]['so_items'] = [data.name] - refs[item_details.bom_no]['planned_qty'] = [data.pending_qty] - + refs[item_details.bom_no] = { + 'qty': data.pending_qty, + 'po_item_ref': data.name, + 'so_details': [] + } + refs[item_details.bom_no]['so_details'].append({ + 'sales_order': data.parent, + 'sales_order_item': data.name, + 'qty': data.pending_qty + }) + pi = self.append('po_items', { - 'name': data.name, 'include_exploded_items': 1, 'warehouse': data.warehouse, 'item_code': data.item_code, @@ -201,8 +209,6 @@ class ProductionPlan(Document): pi.sales_order = data.parent pi.sales_order_item = data.name pi.description = data.description - pi.item_reference = data.name - elif self.get_items_from == "Material Request": pi.material_request = data.parent @@ -210,24 +216,21 @@ class ProductionPlan(Document): pi.description = data.description if refs: - for d in self.po_items: - d.planned_qty = refs[d.bom_no]['qty'] - d.pending_qty = refs[d.bom_no]['qty'] - d.sales_order = '' + for po_item in self.po_items: + po_item.planned_qty = refs[po_item.bom_no]['qty'] + po_item.pending_qty = refs[po_item.bom_no]['qty'] + po_item.sales_order = '' self.add_pp_ref(refs) def add_pp_ref(self, refs): for bom_no in refs: - idx = 0 - for so in refs[bom_no]['so']: + for so_detail in refs[bom_no]['so_details']: self.append('prod_plan_references', { - 'item_reference': refs[bom_no]['ref'], - 'sales_order': so, - 'sales_order_item':refs[bom_no]['so_items'][idx], - 'qty':refs[bom_no]['planned_qty'][idx] + 'item_reference': refs[bom_no]['po_item_ref'], + 'sales_order': so_detail['sales_order'], + 'sales_order_item': so_detail['sales_order_item'], + 'qty': so_detail['qty'] }) - idx+=1 - def calculate_total_planned_qty(self): self.total_planned_qty = 0 diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index ec5c5e0e13..768f99eb43 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -166,16 +166,16 @@ class TestProductionPlan(unittest.TestCase): 'grand_total': so.grand_total }) pln.combine_items = 1 - pln.get_so_items() - for plan_reference in pln.prod_plan_references: - plan_reference.item_reference = pln.po_items[0].name + pln.get_items() pln.submit() - self.assertTrue(pln.po_items[0].planned_qty,3) + self.assertTrue(pln.po_items[0].planned_qty, 3) pln.make_work_order() - work_order = frappe.db.get_value('Work Order', {'production_plan_item': pln.po_items[0].name, - 'production_plan': pln.name,}, 'name') + work_order = frappe.db.get_value('Work Order', { + 'production_plan_item': pln.po_items[0].name, + 'production_plan': pln.name + }, 'name') wo_doc = frappe.get_doc('Work Order', work_order) wo_doc.update({ @@ -194,8 +194,8 @@ class TestProductionPlan(unittest.TestCase): so_wo_qty = frappe.db.get_value('Sales Order Item', so_item, 'work_order_qty') self.assertEqual(so_wo_qty, 0.0) - lat_plan = frappe.get_doc('Production Plan',pln.name) - lat_plan.cancel() + latest_plan = frappe.get_doc('Production Plan', pln.name) + latest_plan.cancel() def test_pp_to_mr_customer_provided(self): #Material Request from Production Plan for Customer Provided diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index bb6450b775..a154464a8b 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -377,7 +377,7 @@ class WorkOrder(Document): total_bundle_qty = 1 prod_plan = frappe.get_doc('Production Plan', self.production_plan) - item_reference = frappe.get_value('Production Plan Item', self.production_plan_item,'item_reference') + item_reference = frappe.get_value('Production Plan Item', self.production_plan_item, 'sales_order_item') for plan_reference in prod_plan.prod_plan_references: work_order_qty = 0.0 From 330353a5ced3d40dc2e699a4481234de205b1fd7 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 22 May 2021 22:07:45 +0530 Subject: [PATCH 012/180] refactor: use frappe.throw instread of recreating _msgprint was basically duplicating behvaiour of frappe.throw --- erpnext/stock/doctype/item/item.py | 25 ++++++------------- .../stock_reconciliation.py | 6 ++--- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index dbac79465e..174c87b48d 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -1068,42 +1068,31 @@ def get_timeline_data(doctype, name): return out -def validate_end_of_life(item_code, end_of_life=None, disabled=None, verbose=1): +def validate_end_of_life(item_code, end_of_life=None, disabled=None): if (not end_of_life) or (disabled is None): end_of_life, disabled = frappe.db.get_value("Item", item_code, ["end_of_life", "disabled"]) if end_of_life and end_of_life != "0000-00-00" and getdate(end_of_life) <= now_datetime().date(): - msg = _("Item {0} has reached its end of life on {1}").format(item_code, formatdate(end_of_life)) - _msgprint(msg, verbose) + frappe.throw(_("Item {0} has reached its end of life on {1}").format(item_code, formatdate(end_of_life))) if disabled: - _msgprint(_("Item {0} is disabled").format(item_code), verbose) + frappe.throw(_("Item {0} is disabled").format(item_code)) -def validate_is_stock_item(item_code, is_stock_item=None, verbose=1): +def validate_is_stock_item(item_code, is_stock_item=None): if not is_stock_item: is_stock_item = frappe.db.get_value("Item", item_code, "is_stock_item") if is_stock_item != 1: - msg = _("Item {0} is not a stock Item").format(item_code) - - _msgprint(msg, verbose) + frappe.throw(_("Item {0} is not a stock Item").format(item_code)) -def validate_cancelled_item(item_code, docstatus=None, verbose=1): +def validate_cancelled_item(item_code, docstatus=None): if docstatus is None: docstatus = frappe.db.get_value("Item", item_code, "docstatus") if docstatus == 2: - msg = _("Item {0} is cancelled").format(item_code) - _msgprint(msg, verbose) - -def _msgprint(msg, verbose): - if verbose: - msgprint(msg, raise_exception=True) - else: - raise frappe.ValidationError(msg) - + frappe.throw(_("Item {0} is cancelled").format(item_code)) def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0): """returns last purchase details in stock uom""" diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 7e216d6181..96b1cadaaf 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -167,8 +167,8 @@ class StockReconciliation(StockController): item = frappe.get_doc("Item", item_code) # end of life and stock item - validate_end_of_life(item_code, item.end_of_life, item.disabled, verbose=0) - validate_is_stock_item(item_code, item.is_stock_item, verbose=0) + validate_end_of_life(item_code, item.end_of_life, item.disabled) + validate_is_stock_item(item_code, item.is_stock_item) # item should not be serialized if item.has_serial_no and not row.serial_no and not item.serial_no_series: @@ -179,7 +179,7 @@ class StockReconciliation(StockController): raise frappe.ValidationError(_("Batch no is required for batched item {0}").format(item_code)) # docstatus should be < 2 - validate_cancelled_item(item_code, item.docstatus, verbose=0) + validate_cancelled_item(item_code, item.docstatus) except Exception as e: self.validation_messages.append(_("Row # ") + ("%d: " % (row.idx)) + cstr(e)) From 4a2dbd4885777e435282df7afe8631f157f7a0a8 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 22 May 2021 22:11:30 +0530 Subject: [PATCH 013/180] refactor: cleanup get_timeline_data, remove py2 --- erpnext/stock/doctype/item/item.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 174c87b48d..61d7e56d13 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -12,7 +12,7 @@ from erpnext.controllers.item_variant import (ItemVariantExistsError, copy_attributes_to_variant, get_variant, make_variant_item_code, validate_item_variant_attributes) from erpnext.setup.doctype.item_group.item_group import (get_parent_item_groups, invalidate_cache_for) from frappe import _, msgprint -from frappe.utils import (cint, cstr, flt, formatdate, get_timestamp, getdate, +from frappe.utils import (cint, cstr, flt, formatdate, getdate, now_datetime, random_string, strip, get_link_to_form, nowtime) from frappe.utils.html_utils import clean_html from frappe.website.doctype.website_slideshow.website_slideshow import \ @@ -21,8 +21,6 @@ from frappe.website.doctype.website_slideshow.website_slideshow import \ from frappe.website.render import clear_cache from frappe.website.website_generator import WebsiteGenerator -from six import iteritems - class DuplicateReorderRows(frappe.ValidationError): pass @@ -1054,18 +1052,15 @@ def make_item_price(item, price_list_name, item_price): }).insert() def get_timeline_data(doctype, name): - '''returns timeline data based on stock ledger entry''' - out = {} - items = dict(frappe.db.sql('''select posting_date, count(*) - from `tabStock Ledger Entry` where item_code=%s - and posting_date > date_sub(curdate(), interval 1 year) - group by posting_date''', name)) + """get timeline data based on Stock Ledger Entry. This is displayed as heatmap on the item page.""" - for date, count in iteritems(items): - timestamp = get_timestamp(date) - out.update({timestamp: count}) + items = frappe.db.sql("""select unix_timestamp(posting_date), count(*) + from `tabStock Ledger Entry` + where item_code=%s and posting_date > date_sub(curdate(), interval 1 year) + group by posting_date""", name) + + return dict(items) - return out def validate_end_of_life(item_code, end_of_life=None, disabled=None): From ad58a8164aeabfe0c87c54052ec5ba3db4c1ca56 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 22 May 2021 22:58:22 +0530 Subject: [PATCH 014/180] refactor: code cleanup minor fixes for improving code quality --- erpnext/stock/doctype/item/item.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 61d7e56d13..0b92e27152 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -201,7 +201,7 @@ class Item(WebsiteGenerator): def make_route(self): if not self.route: return cstr(frappe.db.get_value('Item Group', self.item_group, - 'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5)) + 'route')) + '/' + self.scrub((self.item_name or self.item_code) + '-' + random_string(5)) def validate_website_image(self): if frappe.flags.in_import: @@ -256,7 +256,6 @@ class Item(WebsiteGenerator): "attached_to_name": self.name }) except frappe.DoesNotExistError: - pass # cleanup frappe.local.message_log.pop() From 0b4858d8e5d84723d82544721784d60e8541e3c2 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 22 May 2021 22:59:52 +0530 Subject: [PATCH 015/180] refactor: eliminate unnecessary loop, container casts --- erpnext/stock/doctype/item/item.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 0b92e27152..c41dd67727 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -539,10 +539,7 @@ class Item(WebsiteGenerator): def fill_customer_code(self): """ Append all the customer codes and insert into "customer_code" field of item table """ - cust_code = [] - for d in self.get('customer_items'): - cust_code.append(d.ref_code) - self.customer_code = ','.join(cust_code) + self.customer_code = ','.join(d.ref_code for d in self.get("customer_items", [])) def check_item_tax(self): """Check whether Tax Rate is not entered twice for same Tax Type""" @@ -755,7 +752,7 @@ class Item(WebsiteGenerator): template_item.save() def validate_item_defaults(self): - companies = list(set([row.company for row in self.item_defaults])) + companies = {row.company for row in self.item_defaults} if len(companies) != len(self.item_defaults): frappe.throw(_("Cannot set multiple Item Defaults for a company.")) From 83e6e2e68aec4ef9b6095652a83d19e44bf90e31 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 22 May 2021 23:01:50 +0530 Subject: [PATCH 016/180] refactor: add guard clause for readability --- erpnext/stock/doctype/item/item.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index c41dd67727..b665eb8e46 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -953,20 +953,22 @@ class Item(WebsiteGenerator): d.variant_of = self.variant_of def cant_change(self): - if not self.get("__islocal"): - fields = ("has_serial_no", "is_stock_item", "valuation_method", "has_batch_no") + if self.get("__islocal"): + return - values = frappe.db.get_value("Item", self.name, fields, as_dict=True) - if not values.get('valuation_method') and self.get('valuation_method'): - values['valuation_method'] = frappe.db.get_single_value("Stock Settings", "valuation_method") or "FIFO" + fields = ("has_serial_no", "is_stock_item", "valuation_method", "has_batch_no") - if values: - for field in fields: - if cstr(self.get(field)) != cstr(values.get(field)): - if not self.check_if_linked_document_exists(field): - break # no linked document, allowed - else: - frappe.throw(_("As there are existing transactions against item {0}, you can not change the value of {1}").format(self.name, frappe.bold(self.meta.get_label(field)))) + values = frappe.db.get_value("Item", self.name, fields, as_dict=True) + if not values.get('valuation_method') and self.get('valuation_method'): + values['valuation_method'] = frappe.db.get_single_value("Stock Settings", "valuation_method") or "FIFO" + + if values: + for field in fields: + if cstr(self.get(field)) != cstr(values.get(field)): + if not self.check_if_linked_document_exists(field): + break # no linked document, allowed + else: + frappe.throw(_("As there are existing transactions against item {0}, you can not change the value of {1}").format(self.name, frappe.bold(self.meta.get_label(field)))) def check_if_linked_document_exists(self, field): linked_doctypes = ["Delivery Note Item", "Sales Invoice Item", "POS Invoice Item", "Purchase Receipt Item", From 931c886f92c34453f87b54e315b8f3614a10df48 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 22 May 2021 23:06:16 +0530 Subject: [PATCH 017/180] fix: not checking all fields `break` will break out of the loop without checking remaining fields. --- erpnext/stock/doctype/item/item.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index b665eb8e46..d09a4aa0dc 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -965,9 +965,7 @@ class Item(WebsiteGenerator): if values: for field in fields: if cstr(self.get(field)) != cstr(values.get(field)): - if not self.check_if_linked_document_exists(field): - break # no linked document, allowed - else: + if self.check_if_linked_document_exists(field): frappe.throw(_("As there are existing transactions against item {0}, you can not change the value of {1}").format(self.name, frappe.bold(self.meta.get_label(field)))) def check_if_linked_document_exists(self, field): From 4b484d741d81834ad9749e9395b2510397b7ae09 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 22 May 2021 23:10:54 +0530 Subject: [PATCH 018/180] refactor: use is_new() instead of __islocal Interface over implementation. --- erpnext/stock/doctype/item/item.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index d09a4aa0dc..7906923e6f 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -127,7 +127,7 @@ class Item(WebsiteGenerator): self.cant_change() self.update_show_in_website() - if not self.get("__islocal"): + if not self.is_new(): self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group") self.old_website_item_groups = frappe.db.sql_list("""select item_group from `tabWebsite Item Group` @@ -807,7 +807,7 @@ class Item(WebsiteGenerator): frappe.throw(_("Item has variants.")) def validate_attributes_in_variants(self): - if not self.has_variants or self.get("__islocal"): + if not self.has_variants or self.is_new(): return old_doc = self.get_doc_before_save() @@ -895,7 +895,7 @@ class Item(WebsiteGenerator): frappe.throw(_("Variant Based On cannot be changed")) def validate_uom(self): - if not self.get("__islocal"): + if not self.is_new(): check_stock_uom_with_bin(self.name, self.stock_uom) if self.has_variants: for d in frappe.db.get_all("Item", filters={"variant_of": self.name}): @@ -953,7 +953,7 @@ class Item(WebsiteGenerator): d.variant_of = self.variant_of def cant_change(self): - if self.get("__islocal"): + if self.is_new(): return fields = ("has_serial_no", "is_stock_item", "valuation_method", "has_batch_no") From c229ac932288189366ec6dd57f74db6f34248b1f Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 22 May 2021 23:15:32 +0530 Subject: [PATCH 019/180] refactor: add guard clause for readability Both functions only execute based on a condition. In such cases condition should immediately exit the function, this is called "guard clause" and helps in readability (less indent, and easy to "exit" when reading the code. --- erpnext/stock/doctype/item/item.py | 94 ++++++++++++++++-------------- 1 file changed, 49 insertions(+), 45 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 7906923e6f..f7856be4ae 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -359,47 +359,49 @@ class Item(WebsiteGenerator): context.update(get_slideshow(self)) def set_attribute_context(self, context): - if self.has_variants: - attribute_values_available = {} - context.attribute_values = {} - context.selected_attributes = {} + if not self.has_variants: + return - # load attributes - for v in context.variants: - v.attributes = frappe.get_all("Item Variant Attribute", - fields=["attribute", "attribute_value"], - filters={"parent": v.name}) - # make a map for easier access in templates - v.attribute_map = frappe._dict({}) - for attr in v.attributes: - v.attribute_map[attr.attribute] = attr.attribute_value + attribute_values_available = {} + context.attribute_values = {} + context.selected_attributes = {} - for attr in v.attributes: - values = attribute_values_available.setdefault(attr.attribute, []) - if attr.attribute_value not in values: - values.append(attr.attribute_value) + # load attributes + for v in context.variants: + v.attributes = frappe.get_all("Item Variant Attribute", + fields=["attribute", "attribute_value"], + filters={"parent": v.name}) + # make a map for easier access in templates + v.attribute_map = frappe._dict({}) + for attr in v.attributes: + v.attribute_map[attr.attribute] = attr.attribute_value - if v.name == context.variant.name: - context.selected_attributes[attr.attribute] = attr.attribute_value + for attr in v.attributes: + values = attribute_values_available.setdefault(attr.attribute, []) + if attr.attribute_value not in values: + values.append(attr.attribute_value) - # filter attributes, order based on attribute table - for attr in self.attributes: - values = context.attribute_values.setdefault(attr.attribute, []) + if v.name == context.variant.name: + context.selected_attributes[attr.attribute] = attr.attribute_value - if cint(frappe.db.get_value("Item Attribute", attr.attribute, "numeric_values")): - for val in sorted(attribute_values_available.get(attr.attribute, []), key=flt): - values.append(val) + # filter attributes, order based on attribute table + for attr in self.attributes: + values = context.attribute_values.setdefault(attr.attribute, []) - else: - # get list of values defined (for sequence) - for attr_value in frappe.db.get_all("Item Attribute Value", - fields=["attribute_value"], - filters={"parent": attr.attribute}, order_by="idx asc"): + if cint(frappe.db.get_value("Item Attribute", attr.attribute, "numeric_values")): + for val in sorted(attribute_values_available.get(attr.attribute, []), key=flt): + values.append(val) - if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []): - values.append(attr_value.attribute_value) + else: + # get list of values defined (for sequence) + for attr_value in frappe.db.get_all("Item Attribute Value", + fields=["attribute_value"], + filters={"parent": attr.attribute}, order_by="idx asc"): - context.variant_info = json.dumps(context.variants) + if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []): + values.append(attr_value.attribute_value) + + context.variant_info = json.dumps(context.variants) def set_disabled_attributes(self, context): """Disable selection options of attribute combinations that do not result in a variant""" @@ -736,20 +738,22 @@ class Item(WebsiteGenerator): def update_template_item(self): """Set Show in Website for Template Item if True for its Variant""" - if self.variant_of: - if self.show_in_website: - self.show_variant_in_website = 1 - self.show_in_website = 0 + if not self.variant_of: + return - if self.show_variant_in_website: - # show template - template_item = frappe.get_doc("Item", self.variant_of) + if self.show_in_website: + self.show_variant_in_website = 1 + self.show_in_website = 0 - if not template_item.show_in_website: - template_item.show_in_website = 1 - template_item.flags.dont_update_variants = True - template_item.flags.ignore_permissions = True - template_item.save() + if self.show_variant_in_website: + # show template + template_item = frappe.get_doc("Item", self.variant_of) + + if not template_item.show_in_website: + template_item.show_in_website = 1 + template_item.flags.dont_update_variants = True + template_item.flags.ignore_permissions = True + template_item.save() def validate_item_defaults(self): companies = {row.company for row in self.item_defaults} From bf1b3b89d1cb07481006a94d78112c110be74f70 Mon Sep 17 00:00:00 2001 From: noahjacob Date: Mon, 24 May 2021 17:03:15 +0530 Subject: [PATCH 020/180] refactor: updated conditional visibility of check box --- .../manufacturing/doctype/production_plan/production_plan.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 3041507caf..1c0dde227c 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -322,6 +322,7 @@ }, { "default": "0", + "depends_on": "eval:doc.get_items_from == 'Sales Order'", "fieldname": "combine_items", "fieldtype": "Check", "label": "Consolidate Items" @@ -342,7 +343,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-05-07 16:56:00.255001", + "modified": "2021-05-24 16:59:03.643211", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", From 44c489223b855f837a279c65c989f30500ea70e8 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 22 May 2021 23:43:28 +0530 Subject: [PATCH 021/180] chore: remove py2 compat code --- erpnext/stock/doctype/item/item.py | 4 +--- erpnext/stock/doctype/item/test_item.py | 9 ++++----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index f7856be4ae..a6f5160b5c 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -1,8 +1,6 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - import itertools import json import erpnext diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index e0b89d8e45..c300132ad0 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -14,7 +14,6 @@ from erpnext.stock.doctype.item.item import get_uom_conv_factor from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.get_item_details import get_item_details -from six import iteritems test_ignore = ["BOM"] test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand"] @@ -98,7 +97,7 @@ class TestItem(unittest.TestCase): "ignore_pricing_rule": 1 }) - for key, value in iteritems(to_check): + for key, value in to_check.items(): self.assertEqual(value, details.get(key)) def test_item_tax_template(self): @@ -194,7 +193,7 @@ class TestItem(unittest.TestCase): "plc_conversion_rate": 1, "customer": "_Test Customer", }) - for key, value in iteritems(sales_item_check): + for key, value in sales_item_check.items(): self.assertEqual(value, sales_item_details.get(key)) purchase_item_check = { @@ -215,7 +214,7 @@ class TestItem(unittest.TestCase): "plc_conversion_rate": 1, "supplier": "_Test Supplier", }) - for key, value in iteritems(purchase_item_check): + for key, value in purchase_item_check.items(): self.assertEqual(value, purchase_item_details.get(key)) def test_item_attribute_change_after_variant(self): @@ -464,7 +463,7 @@ class TestItem(unittest.TestCase): self.assertEqual(len(matching_barcodes), 1) details = matching_barcodes[0] - for key, value in iteritems(barcode_properties): + for key, value in barcode_properties.items(): self.assertEqual(value, details.get(key)) # Add barcode again - should cause DuplicateEntryError From ccbde0efa07306710676d144fb7faf29635639db Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 22 May 2021 23:52:00 +0530 Subject: [PATCH 022/180] refactor: use enumerate instead of trackign index also removed dead code --- erpnext/stock/doctype/item/item.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index a6f5160b5c..9a52fb4fc1 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -72,8 +72,6 @@ class Item(WebsiteGenerator): if not self.description: self.description = self.item_name - # if self.is_sales_item and not self.get('is_item_from_hub'): - # self.publish_in_hub = 1 def after_insert(self): '''set opening stock and item price''' @@ -1277,14 +1275,13 @@ def get_item_attribute(parent, attribute_value=''): filters = {'parent': parent, 'attribute_value': ("like", "%%%s%%" % attribute_value)}) def update_variants(variants, template, publish_progress=True): - count=0 - for d in variants: + total = len(variants) + for count, d in enumerate(variants, start=1): variant = frappe.get_doc("Item", d) copy_attributes_to_variant(template, variant) variant.save() - count+=1 if publish_progress: - frappe.publish_progress(count*100/len(variants), title = _("Updating Variants...")) + frappe.publish_progress(count / total * 100, title=_("Updating Variants...")) def on_doctype_update(): # since route is a Text column, it needs a length for indexing From 958d485ba49d47af46324395519d9683cbde4674 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 22 May 2021 23:58:38 +0530 Subject: [PATCH 023/180] refactor: msgprint(raise_exception)->frappe.throw --- erpnext/stock/doctype/item/item.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 9a52fb4fc1..e865bda5c1 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -516,7 +516,7 @@ class Item(WebsiteGenerator): def validate_item_type(self): if self.has_serial_no == 1 and self.is_stock_item == 0 and not self.is_fixed_asset: - msgprint(_("'Has Serial No' can not be 'Yes' for non-stock item"), raise_exception=1) + frappe.throw(_("'Has Serial No' can not be 'Yes' for non-stock item")) if self.has_serial_no == 0 and self.serial_no_series: self.serial_no_series = None @@ -1269,7 +1269,7 @@ def get_uom_conv_factor(uom, stock_uom): @frappe.whitelist() def get_item_attribute(parent, attribute_value=''): if not frappe.has_permission("Item"): - frappe.msgprint(_("No Permission"), raise_exception=1) + frappe.throw(_("No Permission")) return frappe.get_all("Item Attribute Value", fields = ["attribute_value"], filters = {'parent': parent, 'attribute_value': ("like", "%%%s%%" % attribute_value)}) From 297dc5e345b494c6f9cdba12653cdf45721a2af3 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 00:17:08 +0530 Subject: [PATCH 024/180] perf: add basic optimisation for uom conversion --- erpnext/stock/doctype/item/item.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index e865bda5c1..2c862dcfb7 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -1244,6 +1244,9 @@ def get_item_details(item_code, company=None): @frappe.whitelist() def get_uom_conv_factor(uom, stock_uom): + if uom == stock_uom: + return 1.0 + uoms = [uom, stock_uom] value = "" uom_details = frappe.db.sql("""select to_uom, from_uom, value from `tabUOM Conversion Factor`\ From 0d7f54c6c22578797f1e55eb4c29fbc452c591ce Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 00:30:27 +0530 Subject: [PATCH 025/180] refactor: simplify UOM conversion logic - Remove unnecessary sql query - Remove convoluted matching logic and be explcitiy while querying. - better variable names for understanding matching cases --- erpnext/stock/doctype/item/item.py | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 2c862dcfb7..ef855c7db5 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -1247,27 +1247,22 @@ def get_uom_conv_factor(uom, stock_uom): if uom == stock_uom: return 1.0 - uoms = [uom, stock_uom] - value = "" - uom_details = frappe.db.sql("""select to_uom, from_uom, value from `tabUOM Conversion Factor`\ - where to_uom in ({0}) - """.format(', '.join([frappe.db.escape(i, percent=False) for i in uoms])), as_dict=True) + exact_match = frappe.db.get_value("UOM Conversion Factor", {"to_uom": stock_uom, "from_uom": uom}, ["value"], as_dict=1) + if exact_match: + return exact_match.value - for d in uom_details: - if d.from_uom == stock_uom and d.to_uom == uom: - value = 1/flt(d.value) - elif d.from_uom == uom and d.to_uom == stock_uom: - value = d.value + inverse_match = frappe.db.get_value("UOM Conversion Factor", {"to_uom": uom, "from_uom": stock_uom}, ["value"], as_dict=1) + if inverse_match: + return 1 / inverse_match.value - if not value: - uom_stock = frappe.db.get_value("UOM Conversion Factor", {"to_uom": stock_uom}, ["from_uom", "value"], as_dict=1) - uom_row = frappe.db.get_value("UOM Conversion Factor", {"to_uom": uom}, ["from_uom", "value"], as_dict=1) + # This attempts to try and get conversion from intermediate UOM. E.g. mg <=> g <=> kg + uom_stock = frappe.db.get_value("UOM Conversion Factor", {"to_uom": stock_uom}, ["from_uom", "value"], as_dict=1) + uom_row = frappe.db.get_value("UOM Conversion Factor", {"to_uom": uom}, ["from_uom", "value"], as_dict=1) - if uom_stock and uom_row: - if uom_stock.from_uom == uom_row.from_uom: - value = flt(uom_stock.value) * 1/flt(uom_row.value) + if uom_stock and uom_row: + if uom_stock.from_uom == uom_row.from_uom: + return flt(uom_stock.value) * 1/flt(uom_row.value) - return value @frappe.whitelist() def get_item_attribute(parent, attribute_value=''): From 019be66b7b1d137e686ca9b8189f638abdd5f47d Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 01:12:47 +0530 Subject: [PATCH 026/180] fix: consider all UOMs for intermediate conversion - Using `get_value` will restrict intermediate UOM to first UOM that is found. - A self join is required to truly capture the required behaviour. - Add explanation and examples. --- erpnext/stock/doctype/item/item.py | 32 ++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index ef855c7db5..a5bc492422 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -1244,24 +1244,40 @@ def get_item_details(item_code, company=None): @frappe.whitelist() def get_uom_conv_factor(uom, stock_uom): + """ Get UOM conversion factor from uom to stock_uom + e.g. uom = "Kg", stock_uom = "Gram" then returns 1000.0 + """ if uom == stock_uom: return 1.0 - exact_match = frappe.db.get_value("UOM Conversion Factor", {"to_uom": stock_uom, "from_uom": uom}, ["value"], as_dict=1) + from_uom, to_uom = uom, stock_uom # renaming for readability + + exact_match = frappe.db.get_value("UOM Conversion Factor", {"to_uom": to_uom, "from_uom": from_uom}, ["value"], as_dict=1) if exact_match: return exact_match.value - inverse_match = frappe.db.get_value("UOM Conversion Factor", {"to_uom": uom, "from_uom": stock_uom}, ["value"], as_dict=1) + inverse_match = frappe.db.get_value("UOM Conversion Factor", {"to_uom": from_uom, "from_uom": to_uom}, ["value"], as_dict=1) if inverse_match: return 1 / inverse_match.value - # This attempts to try and get conversion from intermediate UOM. E.g. mg <=> g <=> kg - uom_stock = frappe.db.get_value("UOM Conversion Factor", {"to_uom": stock_uom}, ["from_uom", "value"], as_dict=1) - uom_row = frappe.db.get_value("UOM Conversion Factor", {"to_uom": uom}, ["from_uom", "value"], as_dict=1) + # This attempts to try and get conversion from intermediate UOM. + # case: + # g -> mg = 1000 + # g -> kg = 0.001 + # therefore kg -> mg = 1000 / 0.001 = 1,000,000 + intermediate_match = frappe.db.sql(""" + select (first.value / second.value) as value + from `tabUOM Conversion Factor` first + join `tabUOM Conversion Factor` second + on first.from_uom = second.from_uom + where + first.to_uom = %(to_uom)s + and second.to_uom = %(from_uom)s + limit 1 + """, {"to_uom": to_uom, "from_uom": from_uom}, as_dict=1) - if uom_stock and uom_row: - if uom_stock.from_uom == uom_row.from_uom: - return flt(uom_stock.value) * 1/flt(uom_row.value) + if intermediate_match: + return intermediate_match[0].value @frappe.whitelist() From b9fa12d5721dddd00291fca1e9ef157527fb5905 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 12:26:01 +0530 Subject: [PATCH 027/180] test: add tests for uom conversion function --- erpnext/stock/doctype/item/test_item.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index c300132ad0..2366f06f6d 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -374,6 +374,14 @@ class TestItem(unittest.TestCase): self.assertEqual(item_doc.uoms[1].uom, "Kg") self.assertEqual(item_doc.uoms[1].conversion_factor, 1000) + def test_uom_conv_intermediate(self): + factor = get_uom_conv_factor("Pound", "Gram") + self.assertAlmostEqual(factor, 453.592, 3) + + def test_uom_conv_base_case(self): + factor = get_uom_conv_factor("m", "m") + self.assertEqual(factor, 1.0) + def test_item_variant_by_manufacturer(self): fields = [{'field_name': 'description'}, {'field_name': 'variant_based_on'}] set_item_variant_settings(fields) From f5a937bc45e2fe8dd4a18a3b804a86e3caa84cad Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 12:38:15 +0530 Subject: [PATCH 028/180] test: check index creation on item table --- erpnext/stock/doctype/item/test_item.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 2366f06f6d..d9d1e5a44d 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -487,6 +487,20 @@ class TestItem(unittest.TestCase): new_barcode.barcode_type = 'EAN' self.assertRaises(InvalidBarcode, item_doc.save) + def test_index_creation(self): + "check if index is getting created in db" + from erpnext.stock.doctype.item.item import on_doctype_update + on_doctype_update() + + indices = frappe.db.sql("show index from tabItem", as_dict=1) + expected_columns = {"item_code", "item_name", "item_group", "route"} + for index in indices: + expected_columns.discard(index.get("Column_name")) + + if expected_columns: + self.fail(f"Expected db index on these columns: {', '.join(expected_columns)}") + + def set_item_variant_settings(fields): doc = frappe.get_doc('Item Variant Settings') doc.set('fields', fields) From eb177328767c940857f46ca2345e972e9915eda2 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 13:12:16 +0530 Subject: [PATCH 029/180] test: add test for item attribute completion --- erpnext/stock/doctype/item/item.py | 5 +++-- erpnext/stock/doctype/item/test_item.py | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index a5bc492422..ec46f60f2b 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -1281,12 +1281,13 @@ def get_uom_conv_factor(uom, stock_uom): @frappe.whitelist() -def get_item_attribute(parent, attribute_value=''): +def get_item_attribute(parent, attribute_value=""): + """Used for providing auto-completions in child table.""" if not frappe.has_permission("Item"): frappe.throw(_("No Permission")) return frappe.get_all("Item Attribute Value", fields = ["attribute_value"], - filters = {'parent': parent, 'attribute_value': ("like", "%%%s%%" % attribute_value)}) + filters = {'parent': parent, 'attribute_value': ("like", f"%{attribute_value}%")}) def update_variants(variants, template, publish_progress=True): total = len(variants) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index d9d1e5a44d..7cd6050c02 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -10,13 +10,13 @@ from frappe.test_runner import make_test_objects from erpnext.controllers.item_variant import (create_variant, ItemVariantExistsError, InvalidItemAttributeValueError, get_variant) from erpnext.stock.doctype.item.item import StockExistsForTemplate, InvalidBarcode -from erpnext.stock.doctype.item.item import get_uom_conv_factor +from erpnext.stock.doctype.item.item import get_uom_conv_factor, get_item_attribute from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.get_item_details import get_item_details test_ignore = ["BOM"] -test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand"] +test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"] def make_item(item_code, properties=None): if frappe.db.exists("Item", item_code): @@ -500,6 +500,21 @@ class TestItem(unittest.TestCase): if expected_columns: self.fail(f"Expected db index on these columns: {', '.join(expected_columns)}") + def test_attribute_completions(self): + expected_attrs = [{'attribute_value': 'Small'}, + {'attribute_value': 'Extra Small'}, + {'attribute_value': 'Extra Large'}, + {'attribute_value': 'Large'}, + {'attribute_value': '2XL'}, + {'attribute_value': 'Medium'}] + + attrs = get_item_attribute("Test Size") + self.assertEqual(attrs, expected_attrs) + + attrs = get_item_attribute("Test Size", attribute_value="extra") + self.assertEqual(attrs, [{'attribute_value': 'Extra Small'}, {'attribute_value': 'Extra Large'}]) + + def set_item_variant_settings(fields): doc = frappe.get_doc('Item Variant Settings') From a11a8e8ab2e30eaa9f3b1cd80c27dc9ad8f13aeb Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 13:19:22 +0530 Subject: [PATCH 030/180] chore: add blame ignore file --- .git-blame-ignore-revs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..be425ec2d9 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,12 @@ +# Since version 2.23 (released in August 2019), git-blame has a feature +# to ignore or bypass certain commits. +# +# This file contains a list of commits that are not likely what you +# are looking for in a blame, such as mass reformatting or renaming. +# You can set this file as a default ignore file for blame by running +# the following command. +# +# $ git config blame.ignoreRevsFile .git-blame-ignore-revs + +# This commit just changes spaces to tabs for indentation in some files +5f473611bd6ed57703716244a054d3fb5ba9cd23 From 4e360f805f5cb4f7ed500316aa97ca7e52b2f9bf Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 14:48:03 +0530 Subject: [PATCH 031/180] test: hoist defaults to function signature --- erpnext/stock/doctype/item/test_item.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 7cd6050c02..9694927914 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -530,23 +530,24 @@ def make_item_variant(): test_records = frappe.get_test_records('Item') -def create_item(item_code, is_stock_item=None, valuation_rate=0, warehouse=None, is_customer_provided_item=None, - customer=None, is_purchase_item=None, opening_stock=None, company=None): +def create_item(item_code, is_stock_item=1, valuation_rate=0, warehouse="_Test Warehouse - _TC", + is_customer_provided_item=None, customer=None, is_purchase_item=None, opening_stock=0, + company="_Test Company"): if not frappe.db.exists("Item", item_code): item = frappe.new_doc("Item") item.item_code = item_code item.item_name = item_code item.description = item_code item.item_group = "All Item Groups" - item.is_stock_item = is_stock_item or 1 - item.opening_stock = opening_stock or 0 - item.valuation_rate = valuation_rate or 0.0 + item.is_stock_item = is_stock_item + item.opening_stock = opening_stock + item.valuation_rate = valuation_rate item.is_purchase_item = is_purchase_item item.is_customer_provided_item = is_customer_provided_item item.customer = customer or '' item.append("item_defaults", { - "default_warehouse": warehouse or '_Test Warehouse - _TC', - "company": company or "_Test Company" + "default_warehouse": warehouse, + "company": company }) item.save() else: From fc54cf68ac33b213dd44821c10e01f84a6c4727a Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 15:04:41 +0530 Subject: [PATCH 032/180] test: add tests for checking stock_uom with bin --- erpnext/stock/doctype/item/test_item.py | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 9694927914..8df12a3f16 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -514,6 +514,35 @@ class TestItem(unittest.TestCase): attrs = get_item_attribute("Test Size", attribute_value="extra") self.assertEqual(attrs, [{'attribute_value': 'Extra Small'}, {'attribute_value': 'Extra Large'}]) + def test_check_stock_uom_with_bin(self): + # this item has opening stock and stock_uom set in test_records. + item = frappe.get_doc("Item", "_Test Item") + item.stock_uom = "Gram" + self.assertRaises(frappe.ValidationError, item.save) + + def test_check_stock_uom_with_bin_no_sle(self): + from erpnext.stock.stock_balance import update_bin_qty + item = create_item("_Item with bin qty") + item.stock_uom = "Gram" + item.save() + + update_bin_qty(item.item_code, "_Test Warehouse - _TC", { + "reserved_qty": 10 + }) + + item.stock_uom = "Kilometer" + self.assertRaises(frappe.ValidationError, item.save) + + update_bin_qty(item.item_code, "_Test Warehouse - _TC", { + "reserved_qty": 0 + }) + + item.load_from_db() + item.stock_uom = "Kilometer" + try: + item.save() + except frappe.ValidationError as e: + self.fail(f"UoM change not allowed even though no SLE / BIN with positive qty exists: {e}") def set_item_variant_settings(fields): From 57266a7343edd1fb963d20db28593bed3f80ae50 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 14:21:24 +0530 Subject: [PATCH 033/180] refactor: check_stock_uom_with_bin --- erpnext/stock/doctype/item/item.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index ec46f60f2b..a0bd49543e 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -1183,27 +1183,25 @@ def check_stock_uom_with_bin(item, stock_uom): if stock_uom == frappe.db.get_value("Item", item, "stock_uom"): return - matched = True ref_uom = frappe.db.get_value("Stock Ledger Entry", {"item_code": item}, "stock_uom") if ref_uom: if cstr(ref_uom) != cstr(stock_uom): - matched = False - else: - bin_list = frappe.db.sql("select * from tabBin where item_code=%s", item, as_dict=1) - for bin in bin_list: - if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0 - or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(stock_uom): - matched = False - break + frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.").format(item)) - if matched and bin_list: - frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item)) + bin_list = frappe.db.sql(""" + select * from tabBin where item_code = %s + and (reserved_qty > 0 or ordered_qty > 0 or indented_qty > 0 or planned_qty > 0) + and stock_uom != %s + """, (item, stock_uom), as_dict=1) + + if bin_list: + frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You need to either cancel the linked documents or create a new Item.").format(item)) + + # No SLE or documents against item. Bin UOM can be changed safely. + frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item)) - if not matched: - frappe.throw( - _("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.").format(item)) def get_item_defaults(item_code, company): item = frappe.get_cached_doc('Item', item_code) From e971b4592e3bb1894294f561a522f2b06336908b Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 15:33:06 +0530 Subject: [PATCH 034/180] test: add test for is_stock_item --- erpnext/stock/doctype/item/test_item.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 8df12a3f16..d9c77efc24 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -10,7 +10,8 @@ from frappe.test_runner import make_test_objects from erpnext.controllers.item_variant import (create_variant, ItemVariantExistsError, InvalidItemAttributeValueError, get_variant) from erpnext.stock.doctype.item.item import StockExistsForTemplate, InvalidBarcode -from erpnext.stock.doctype.item.item import get_uom_conv_factor, get_item_attribute +from erpnext.stock.doctype.item.item import (get_uom_conv_factor, get_item_attribute, + validate_is_stock_item) from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.get_item_details import get_item_details @@ -544,6 +545,13 @@ class TestItem(unittest.TestCase): except frappe.ValidationError as e: self.fail(f"UoM change not allowed even though no SLE / BIN with positive qty exists: {e}") + def test_validate_stock_item(self): + self.assertRaises(frappe.ValidationError, validate_is_stock_item, "_Test Non Stock Item") + + try: + validate_is_stock_item("_Test Item") + except frappe.ValidationError as e: + self.fail(f"stock item considered non-stock item: {e}") def set_item_variant_settings(fields): doc = frappe.get_doc('Item Variant Settings') From 42e057d079c1807393d376d347762e97100b6883 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 15:45:25 +0530 Subject: [PATCH 035/180] test: add test for get_timeline_data in item --- erpnext/stock/doctype/item/test_item.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index d9c77efc24..234a9132c2 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -11,7 +11,7 @@ from erpnext.controllers.item_variant import (create_variant, ItemVariantExistsE InvalidItemAttributeValueError, get_variant) from erpnext.stock.doctype.item.item import StockExistsForTemplate, InvalidBarcode from erpnext.stock.doctype.item.item import (get_uom_conv_factor, get_item_attribute, - validate_is_stock_item) + validate_is_stock_item, get_timeline_data) from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.get_item_details import get_item_details @@ -488,6 +488,20 @@ class TestItem(unittest.TestCase): new_barcode.barcode_type = 'EAN' self.assertRaises(InvalidBarcode, item_doc.save) + def test_heatmap_data(self): + import time + data = get_timeline_data("Item", "_Test Item") + self.assertTrue(isinstance(data, dict)) + + now = time.time() + one_year_ago = now - 366 * 24 * 60 * 60 + + for timestamp, count in data.items(): + self.assertIsInstance(timestamp, int) + self.assertTrue(one_year_ago <= timestamp <= now) + self.assertIsInstance(count, int) + self.assertTrue(count >= 0) + def test_index_creation(self): "check if index is getting created in db" from erpnext.stock.doctype.item.item import on_doctype_update From 76dd6e904682a1bec1ff21d66c45e164fd26a47b Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 16:19:48 +0530 Subject: [PATCH 036/180] test: contextmanager to change settings --- erpnext/tests/utils.py | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py index 16ecd5180b..11eb6afc1f 100644 --- a/erpnext/tests/utils.py +++ b/erpnext/tests/utils.py @@ -1,7 +1,8 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals +import copy +from contextlib import contextmanager import frappe @@ -41,3 +42,38 @@ def create_test_contact_and_address(): contact.add_email("test_contact_customer@example.com", is_primary=True) contact.add_phone("+91 0000000000", is_primary_phone=True) contact.insert() + + +@contextmanager +def change_settings(doctype, settings_dict): + """ A context manager to ensure that settings are changed before running + function and restored after running it regardless of exceptions occured. + This is useful in tests where you want to make changes in a function but + don't retain those changes. + import and use as decorator to cover full function or using `with` statement. + + example: + @change_settings("Stock Settings", {"item_naming_by": "Naming Series"}) + def test_case(self): + ... + """ + + try: + settings = frappe.get_doc(doctype) + # remember setting + previous_settings = copy.deepcopy(settings_dict) + for key in previous_settings: + previous_settings[key] = getattr(settings, key) + + # change setting + for key, value in settings_dict.items(): + setattr(settings, key, value) + settings.save() + yield # yield control to calling function + + finally: + # restore settings + settings = frappe.get_doc(doctype) + for key, value in previous_settings.items(): + setattr(settings, key, value) + settings.save() From c15fef571fda7fa6bf2ae7f43380b29098775d87 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 16:26:37 +0530 Subject: [PATCH 037/180] test: item naming series behaviour --- erpnext/stock/doctype/item/test_item.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 234a9132c2..9adacdfb78 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -14,6 +14,7 @@ from erpnext.stock.doctype.item.item import (get_uom_conv_factor, get_item_attri validate_is_stock_item, get_timeline_data) from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.get_item_details import get_item_details +from erpnext.tests.utils import change_settings test_ignore = ["BOM"] @@ -567,6 +568,13 @@ class TestItem(unittest.TestCase): except frappe.ValidationError as e: self.fail(f"stock item considered non-stock item: {e}") + @change_settings("Stock Settings", {"item_naming_by": "Naming Series"}) + def test_autoname_series(self): + item = frappe.new_doc("Item") + item.item_group = "All Item Groups" + item.save() # if item code saved without item_code then series worked + + def set_item_variant_settings(fields): doc = frappe.get_doc('Item Variant Settings') doc.set('fields', fields) From 3aed662f4690ad6fb5fda680aad4246103eded81 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 16:41:03 +0530 Subject: [PATCH 038/180] chore: translation / semgrep / sider fixes --- erpnext/stock/doctype/item/item.py | 6 +++--- erpnext/stock/doctype/item/test_item.py | 10 +++++----- .../stock_reconciliation/stock_reconciliation.py | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index a0bd49543e..dd815404fa 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -365,8 +365,8 @@ class Item(WebsiteGenerator): # load attributes for v in context.variants: v.attributes = frappe.get_all("Item Variant Attribute", - fields=["attribute", "attribute_value"], - filters={"parent": v.name}) + fields=["attribute", "attribute_value"], + filters={"parent": v.name}) # make a map for easier access in templates v.attribute_map = frappe._dict({}) for attr in v.attributes: @@ -1256,7 +1256,7 @@ def get_uom_conv_factor(uom, stock_uom): inverse_match = frappe.db.get_value("UOM Conversion Factor", {"to_uom": from_uom, "from_uom": to_uom}, ["value"], as_dict=1) if inverse_match: - return 1 / inverse_match.value + return 1 / inverse_match.value # This attempts to try and get conversion from intermediate UOM. # case: diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 9adacdfb78..406039dc58 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -518,11 +518,11 @@ class TestItem(unittest.TestCase): def test_attribute_completions(self): expected_attrs = [{'attribute_value': 'Small'}, - {'attribute_value': 'Extra Small'}, - {'attribute_value': 'Extra Large'}, - {'attribute_value': 'Large'}, - {'attribute_value': '2XL'}, - {'attribute_value': 'Medium'}] + {'attribute_value': 'Extra Small'}, + {'attribute_value': 'Extra Large'}, + {'attribute_value': 'Large'}, + {'attribute_value': '2XL'}, + {'attribute_value': 'Medium'}] attrs = get_item_attribute("Test Size") self.assertEqual(attrs, expected_attrs) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 96b1cadaaf..b9f91906c6 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -96,7 +96,7 @@ class StockReconciliation(StockController): def validate_data(self): def _get_msg(row_num, msg): - return _("Row # {0}: ").format(row_num+1) + msg + return _("Row # {0}:").format(row_num+1) + " " + msg self.validation_messages = [] item_warehouse_combinations = [] @@ -182,7 +182,7 @@ class StockReconciliation(StockController): validate_cancelled_item(item_code, item.docstatus) except Exception as e: - self.validation_messages.append(_("Row # ") + ("%d: " % (row.idx)) + cstr(e)) + self.validation_messages.append(_("Row #") + " " + ("%d: " % (row.idx)) + cstr(e)) def update_stock_ledger(self): """ find difference between current and expected entries From 15f8a0fb22addd730b1ebfb43635cfb29f1ddb90 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 23 May 2021 18:10:21 +0530 Subject: [PATCH 039/180] test: fix flaky test --- erpnext/stock/doctype/item/test_item.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 406039dc58..c7467a5a0f 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -517,18 +517,15 @@ class TestItem(unittest.TestCase): self.fail(f"Expected db index on these columns: {', '.join(expected_columns)}") def test_attribute_completions(self): - expected_attrs = [{'attribute_value': 'Small'}, - {'attribute_value': 'Extra Small'}, - {'attribute_value': 'Extra Large'}, - {'attribute_value': 'Large'}, - {'attribute_value': '2XL'}, - {'attribute_value': 'Medium'}] + expected_attrs = {"Small", "Extra Small", "Extra Large", "Large", "2XL", "Medium"} attrs = get_item_attribute("Test Size") - self.assertEqual(attrs, expected_attrs) + received_attrs = {attr.attribute_value for attr in attrs} + self.assertEqual(received_attrs, expected_attrs) attrs = get_item_attribute("Test Size", attribute_value="extra") - self.assertEqual(attrs, [{'attribute_value': 'Extra Small'}, {'attribute_value': 'Extra Large'}]) + received_attrs = {attr.attribute_value for attr in attrs} + self.assertEqual(received_attrs, {"Extra Small", "Extra Large"}) def test_check_stock_uom_with_bin(self): # this item has opening stock and stock_uom set in test_records. From c9aa7268187748c0cbb5189768d463b71b9880f5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 16 Sep 2020 20:30:24 +0530 Subject: [PATCH 040/180] feat: Tax deduction against advance payments --- .../advance_taxes_and_charges/__init__.py | 0 .../advance_taxes_and_charges.json | 201 ++++++++++++++++++ .../advance_taxes_and_charges.py | 10 + 3 files changed, 211 insertions(+) create mode 100644 erpnext/accounts/doctype/advance_taxes_and_charges/__init__.py create mode 100644 erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json create mode 100644 erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.py diff --git a/erpnext/accounts/doctype/advance_taxes_and_charges/__init__.py b/erpnext/accounts/doctype/advance_taxes_and_charges/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json new file mode 100644 index 0000000000..79eda8c5fe --- /dev/null +++ b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json @@ -0,0 +1,201 @@ +{ + "actions": [], + "creation": "2020-09-12 22:26:19.594367", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "add_deduct_tax", + "charge_type", + "row_id", + "account_head", + "col_break_1", + "description", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break", + "section_break_8", + "rate", + "section_break_9", + "tax_amount", + "total", + "column_break_13", + "base_tax_amount", + "base_total" + ], + "fields": [ + { + "columns": 2, + "fieldname": "charge_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Type", + "oldfieldname": "charge_type", + "oldfieldtype": "Select", + "options": "\nActual\nOn Paid Amount\nOn Previous Row Amount\nOn Previous Row Total", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "depends_on": "eval:[\"On Previous Row Amount\", \"On Previous Row Total\"].indexOf(doc.charge_type)!==-1", + "fieldname": "row_id", + "fieldtype": "Data", + "label": "Reference Row #", + "oldfieldname": "row_id", + "oldfieldtype": "Data", + "show_days": 1, + "show_seconds": 1 + }, + { + "columns": 2, + "fieldname": "account_head", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Account Head", + "oldfieldname": "account_head", + "oldfieldtype": "Link", + "options": "Account", + "reqd": 1, + "search_index": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "col_break_1", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, + "width": "50%" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Small Text", + "print_width": "300px", + "reqd": 1, + "show_days": 1, + "show_seconds": 1, + "width": "300px" + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": ":Company", + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "oldfieldname": "cost_center_other_charges", + "oldfieldtype": "Link", + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "columns": 2, + "fieldname": "rate", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Rate", + "oldfieldname": "rate", + "oldfieldtype": "Currency", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "columns": 2, + "fieldname": "tax_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "currency", + "show_days": 1, + "show_seconds": 1 + }, + { + "columns": 2, + "fieldname": "total", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total", + "options": "currency", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "base_tax_amount", + "fieldtype": "Currency", + "label": "Amount (Company Currency)", + "oldfieldname": "tax_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "base_total", + "fieldtype": "Currency", + "label": "Total (Company Currency)", + "oldfieldname": "total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "add_deduct_tax", + "fieldtype": "Select", + "label": "Add Or Deduct", + "options": "Add\nDeduct", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-09-12 22:30:36.150935", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Advance Taxes and Charges", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "ASC" +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.py b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.py new file mode 100644 index 0000000000..597d2ccc62 --- /dev/null +++ b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.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 AdvanceTaxesandCharges(Document): + pass From 61c5e478afada33b5d8ffe43fc174d345f581e03 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 1 Oct 2020 13:56:55 +0530 Subject: [PATCH 041/180] feat: Tax deduction against advance payments --- .../doctype/payment_entry/payment_entry.js | 137 ++++++- .../doctype/payment_entry/payment_entry.json | 358 ++++++++++++++---- .../doctype/payment_entry/payment_entry.py | 114 +++++- .../payment_entry_deduction.json | 196 +++------- erpnext/controllers/accounts_controller.py | 10 + erpnext/public/js/controllers/accounts.js | 2 +- 6 files changed, 607 insertions(+), 210 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index b80e8ada38..29e4a31dad 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -3,6 +3,8 @@ {% include "erpnext/public/js/controllers/accounts.js" %} frappe.provide("erpnext.accounts.dimensions"); +cur_frm.cscript.tax_table = "Advance Taxes and Charges"; + frappe.ui.form.on('Payment Entry', { onload: function(frm) { if(frm.doc.__islocal) { @@ -182,6 +184,8 @@ frappe.ui.form.on('Payment Entry', { frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)); frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency); + frm.toggle_display("base_total_taxes_and_charges", frm.doc.total_taxes_and_charges && + (frm.doc.paid_from_account_currency != company_currency)); frm.toggle_display("base_received_amount", ( frm.doc.paid_to_account_currency != company_currency @@ -843,12 +847,12 @@ frappe.ui.form.on('Payment Entry', { if(frm.doc.payment_type == "Receive" && frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions && frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) { - unallocated_amount = (frm.doc.base_received_amount + total_deductions - - frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate; + unallocated_amount = (frm.doc.base_received_amount + total_deductions + frm.doc.base_total_taxes_and_charges + + frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate; } else if (frm.doc.payment_type == "Pay" && frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions && frm.doc.total_allocated_amount < frm.doc.received_amount + (total_deductions / frm.doc.target_exchange_rate)) { - unallocated_amount = (frm.doc.base_paid_amount - (total_deductions + unallocated_amount = (frm.doc.base_paid_amount + frm.doc.base_total_taxes_and_charges - (total_deductions + frm.doc.base_total_allocated_amount)) / frm.doc.target_exchange_rate; } } @@ -874,7 +878,8 @@ frappe.ui.form.on('Payment Entry', { var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [], function(d) { return flt(d.amount) })); - frm.set_value("difference_amount", difference_amount - total_deductions); + frm.set_value("difference_amount", difference_amount - total_deductions + + frm.doc.base_total_taxes_and_charges); frm.events.hide_unhide_fields(frm); }, @@ -1002,7 +1007,107 @@ frappe.ui.form.on('Payment Entry', { } }); } - } + }, + + sales_taxes_and_charges_template: function(frm) { + frm.trigger('fetch_taxes_from_template'); + }, + + purchase_taxes_and_charges_template: function(frm) { + frm.trigger('fetch_taxes_from_template'); + }, + + fetch_taxes_from_template: function(frm) { + let master_doctype = ''; + let taxes_and_charges = ''; + + if (frm.doc.party_type == 'Supplier') { + master_doctype = 'Purchase Taxes and Charges Template'; + taxes_and_charges = frm.doc.purchase_taxes_and_charges_template; + } else if (frm.doc.party_type == 'Customer') { + master_doctype = 'Sales Taxes and Charges Template'; + taxes_and_charges = frm.doc.sales_taxes_and_charges_template; + } + + if (!taxes_and_charges) { + return; + } + + frappe.call({ + method: "erpnext.controllers.accounts_controller.get_taxes_and_charges", + args: { + "master_doctype": master_doctype, + "master_name": taxes_and_charges + }, + callback: function(r) { + if(!r.exc && r.message) { + // set taxes table + if(r.message) { + for (let tax of r.message) { + if (tax.charge_type === 'On Net Total') { + tax.charge_type = 'On Paid Amount'; + } + me.frm.add_child("taxes", tax); + } + frm.trigger('calculate_taxes'); + frm.events.set_unallocated_amount(frm); + } + } + } + }); + }, + + calculate_taxes: function(frm) { + frm.doc.total_taxes_and_charges = 0.0; + frm.doc.base_total_taxes_and_charges = 0.0; + + $.each(me.frm.doc["taxes"] || [], function(i, tax) { + let tax_rate = tax.rate; + let current_tax_amount = 0.0; + + // To set row_id by default as previous row. + if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) { + if (tax.idx === 1) { + frappe.throw(__("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")); + } + if (!tax.row_id) { + tax.row_id = tax.idx - 1; + } + } + + if(tax.charge_type == "Actual") { + current_tax_amount = flt(tax.tax_amount, precision("tax_amount", tax)); + } else if(tax.charge_type == "On Paid Amount") { + current_tax_amount = (tax_rate / 100.0) * frm.doc.paid_amount; + } else if(tax.charge_type == "On Previous Row Amount") { + current_tax_amount = (tax_rate / 100.0) * + frm.doc["taxes"][cint(tax.row_id) - 1].tax_amount; + + } else if(tax.charge_type == "On Previous Row Total") { + current_tax_amount = (tax_rate / 100.0) * + frm.doc["taxes"][cint(tax.row_id) - 1].total; + } + + tax.tax_amount = current_tax_amount; + tax.base_tax_amount = tax.tax_amount * frm.doc.source_exchange_rate; + + current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; + + if(i==0) { + tax.total = flt(frm.doc.paid_amount + current_tax_amount, precision("total", tax)); + } else { + tax.total = flt(frm.doc["taxes"][i-1].total + current_tax_amount, precision("total", tax)); + } + + tax.base_total = tax.total * frm.doc.source_exchange_rate; + frm.doc.total_taxes_and_charges += current_tax_amount; + frm.doc.base_total_taxes_and_charges += current_tax_amount * frm.doc.source_exchange_rate; + + frm.refresh_field('taxes'); + frm.refresh_field('total_taxes_and_charges'); + frm.refresh_field('base_total_taxes_and_charges'); + }); + }, }); @@ -1049,6 +1154,28 @@ frappe.ui.form.on('Payment Entry Reference', { } }) +frappe.ui.form.on('Advance Taxes and Charges', { + rate: function(frm) { + frm.events.calculate_taxes(frm); + frm.events.set_unallocated_amount(frm); + }, + + tax_amount : function(frm) { + frm.events.calculate_taxes(frm); + frm.events.set_unallocated_amount(frm); + }, + + row_id: function(frm) { + frm.events.calculate_taxes(frm); + frm.events.set_unallocated_amount(frm); + }, + + taxes_remove: function(frm) { + frm.events.calculate_taxes(frm); + frm.events.set_unallocated_amount(frm); + } +}) + frappe.ui.form.on('Payment Entry Deduction', { amount: function(frm) { frm.events.set_unallocated_amount(frm); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 328584a61a..11ae17093f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -24,6 +24,10 @@ "party_bank_account", "contact_person", "contact_email", + "tds_details_section", + "tax_withholding_category", + "column_break_20", + "apply_tax_withholding_amount", "payment_accounts_section", "party_balance", "paid_from", @@ -52,6 +56,12 @@ "unallocated_amount", "difference_amount", "write_off_difference_amount", + "taxes_and_charges_section", + "purchase_taxes_and_charges_template", + "sales_taxes_and_charges_template", + "taxes", + "base_total_taxes_and_charges", + "total_taxes_and_charges", "deductions_or_loss_section", "deductions", "transaction_references", @@ -82,7 +92,9 @@ { "fieldname": "type_of_payment", "fieldtype": "Section Break", - "label": "Type of Payment" + "label": "Type of Payment", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -92,7 +104,9 @@ "options": "ACC-PAY-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -103,11 +117,15 @@ "label": "Payment Type", "options": "Receive\nPay\nInternal Transfer", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_5", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -116,7 +134,9 @@ "fieldtype": "Date", "in_list_view": 1, "label": "Posting Date", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -125,26 +145,34 @@ "options": "Company", "print_hide": 1, "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cost_center", "fieldtype": "Link", "label": "Cost Center", - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "mode_of_payment", "fieldtype": "Link", "in_list_view": 1, "label": "Mode of Payment", - "options": "Mode of Payment" + "options": "Mode of Payment", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type)", "fieldname": "party_section", "fieldtype": "Section Break", - "label": "Payment From / To" + "label": "Payment From / To", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type) && doc.docstatus==0", @@ -154,7 +182,9 @@ "label": "Party Type", "options": "DocType", "print_hide": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -163,7 +193,9 @@ "fieldtype": "Dynamic Link", "in_standard_filter": 1, "label": "Party", - "options": "party_type" + "options": "party_type", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -171,18 +203,24 @@ "fieldname": "party_name", "fieldtype": "Data", "in_global_search": 1, - "label": "Party Name" + "label": "Party Name", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_11", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "party", "fieldname": "contact_person", "fieldtype": "Link", "label": "Contact", - "options": "Contact" + "options": "Contact", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "contact_person", @@ -190,13 +228,17 @@ "fieldtype": "Data", "label": "Email", "options": "Email", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "payment_accounts_section", "fieldtype": "Section Break", - "label": "Accounts" + "label": "Accounts", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "party", @@ -204,7 +246,9 @@ "fieldtype": "Currency", "label": "Party Balance", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -215,7 +259,9 @@ "label": "Account Paid From", "options": "Account", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "paid_from", @@ -225,7 +271,9 @@ "options": "Currency", "print_hide": 1, "read_only": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "paid_from", @@ -234,11 +282,15 @@ "label": "Account Balance", "options": "paid_from_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_18", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:(in_list([\"Internal Transfer\", \"Receive\"], doc.payment_type) || doc.party)", @@ -248,7 +300,9 @@ "label": "Account Paid To", "options": "Account", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "paid_to", @@ -258,7 +312,9 @@ "options": "Currency", "print_hide": 1, "read_only": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "paid_to", @@ -267,13 +323,17 @@ "label": "Account Balance", "options": "paid_to_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:(doc.paid_to && doc.paid_from)", "fieldname": "payment_amounts_section", "fieldtype": "Section Break", - "label": "Amount" + "label": "Amount", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -281,14 +341,18 @@ "fieldtype": "Currency", "label": "Paid Amount", "options": "paid_from_account_currency", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "source_exchange_rate", "fieldtype": "Float", "label": "Exchange Rate", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_paid_amount", @@ -297,11 +361,15 @@ "options": "Company:company:default_currency", "print_hide": 1, "read_only": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_21", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -310,14 +378,18 @@ "label": "Received Amount", "options": "paid_to_account_currency", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "target_exchange_rate", "fieldtype": "Float", "label": "Exchange Rate", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_received_amount", @@ -326,30 +398,40 @@ "options": "Company:company:default_currency", "print_hide": 1, "read_only": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)", "fieldname": "section_break_14", "fieldtype": "Section Break", - "label": "Reference" + "label": "Reference", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.docstatus==0", "fieldname": "get_outstanding_invoice", "fieldtype": "Button", - "label": "Get Outstanding Invoice" + "label": "Get Outstanding Invoice", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "references", "fieldtype": "Table", "label": "Payment References", - "options": "Payment Entry Reference" + "options": "Payment Entry Reference", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_34", "fieldtype": "Section Break", - "label": "Writeoff" + "label": "Writeoff", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -358,7 +440,9 @@ "fieldtype": "Currency", "label": "Total Allocated Amount", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_allocated_amount", @@ -366,23 +450,31 @@ "label": "Total Allocated Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "set_exchange_gain_loss", "fieldtype": "Button", - "label": "Set Exchange Gain / Loss" + "label": "Set Exchange Gain / Loss", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_36", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:(doc.paid_amount && doc.received_amount && doc.references)", "fieldname": "unallocated_amount", "fieldtype": "Currency", "label": "Unallocated Amount", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -392,13 +484,17 @@ "label": "Difference Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "difference_amount", "fieldname": "write_off_difference_amount", "fieldtype": "Button", - "label": "Write Off Difference Amount" + "label": "Write Off Difference Amount", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -406,29 +502,39 @@ "depends_on": "eval:(doc.paid_amount && doc.received_amount)", "fieldname": "deductions_or_loss_section", "fieldtype": "Section Break", - "label": "Deductions or Loss" + "label": "Deductions or Loss", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "deductions", "fieldtype": "Table", "label": "Payment Deductions or Loss", - "options": "Payment Entry Deduction" + "options": "Payment Entry Deduction", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "transaction_references", "fieldtype": "Section Break", - "label": "Transaction ID" + "label": "Transaction ID", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, "depends_on": "eval:(doc.paid_from && doc.paid_to)", "fieldname": "reference_no", "fieldtype": "Data", - "label": "Cheque/Reference No" + "label": "Cheque/Reference No", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_23", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -436,7 +542,9 @@ "fieldname": "reference_date", "fieldtype": "Date", "label": "Cheque/Reference Date", - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.docstatus==1", @@ -445,21 +553,27 @@ "label": "Clearance Date", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "depends_on": "eval:(doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)", "fieldname": "section_break_12", "fieldtype": "Section Break", - "label": "More Information" + "label": "More Information", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "project", "fieldtype": "Link", "label": "Project", "options": "Project", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "remarks", @@ -470,33 +584,43 @@ }, { "fieldname": "column_break_16", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "letter_head", "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "print_heading", "fieldtype": "Link", "label": "Print Heading", "options": "Print Heading", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "bank_account.bank", "fieldname": "bank", "fieldtype": "Read Only", - "label": "Bank" + "label": "Bank", + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "bank_account.bank_account_no", "fieldname": "bank_account_no", "fieldtype": "Read Only", - "label": "Bank Account No" + "label": "Bank Account No", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_order", @@ -505,12 +629,16 @@ "no_copy": 1, "options": "Payment Order", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section" + "label": "Subscription Section", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -520,7 +648,9 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -529,7 +659,9 @@ "no_copy": 1, "options": "Payment Entry", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "title", @@ -544,14 +676,18 @@ "fieldname": "bank_account", "fieldtype": "Link", "label": "Company Bank Account", - "options": "Bank Account" + "options": "Bank Account", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "party", "fieldname": "party_bank_account", "fieldtype": "Link", "label": "Party Bank Account", - "options": "Bank Account" + "options": "Bank Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_order_status", @@ -559,17 +695,23 @@ "hidden": 1, "label": "Payment Order Status", "options": "Initiated\nPayment Ordered", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", - "label": "Accounting Dimensions" + "label": "Accounting Dimensions", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "dimension_col_break", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -584,12 +726,94 @@ "fieldname": "custom_remarks", "fieldtype": "Check", "label": "Custom Remarks" + }, + { + "depends_on": "eval: doc.payment_type == 'Pay' && doc.party_type == 'Supplier'", + "fieldname": "tds_details_section", + "fieldtype": "Section Break", + "label": "TDS Details", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "tax_withholding_category", + "fieldtype": "Link", + "label": "Tax Withholding Category", + "options": "Tax Withholding Category", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "fieldname": "apply_tax_withholding_amount", + "fieldtype": "Check", + "label": "Apply Tax Withholding Amount", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "column_break_20", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "collapsible": 1, + "fieldname": "taxes_and_charges_section", + "fieldtype": "Section Break", + "label": "Taxes and Charges", + "show_days": 1, + "show_seconds": 1 + }, + { + "depends_on": "eval:doc.party_type == 'Supplier'", + "fieldname": "purchase_taxes_and_charges_template", + "fieldtype": "Link", + "label": "Taxes and Charges Template", + "options": "Purchase Taxes and Charges Template", + "show_days": 1, + "show_seconds": 1 + }, + { + "depends_on": "eval: doc.party_type == 'Customer'", + "fieldname": "sales_taxes_and_charges_template", + "fieldtype": "Link", + "label": "Taxes and Charges Template", + "options": "Sales Taxes and Charges Template", + "show_days": 1, + "show_seconds": 1 + }, + { + "depends_on": "eval: doc.party_type == 'Supplier' || doc.party_type == 'Customer'", + "fieldname": "taxes", + "fieldtype": "Table", + "label": "Advance Taxes and Charges", + "options": "Advance Taxes and Charges", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "base_total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges (Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-03-08 13:05:16.958866", + "modified": "2020-09-13 22:33:59.860146", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 62ab76c323..a41b4512a1 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe, erpnext, json from frappe import _, scrub, ValidationError -from frappe.utils import flt, comma_or, nowdate, getdate +from frappe.utils import flt, comma_or, nowdate, getdate, cint from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on from erpnext.accounts.party import get_party_account from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account @@ -15,6 +15,7 @@ from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amo from erpnext.accounts.doctype.bank_account.bank_account import get_party_bank_account, get_bank_account_details from erpnext.controllers.accounts_controller import AccountsController, get_supplier_block_status from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting +from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details from six import string_types, iteritems @@ -52,6 +53,8 @@ class PaymentEntry(AccountsController): self.set_exchange_rate() self.validate_mandatory() self.validate_reference_documents() + self.set_tax_withholding() + self.calculate_taxes() self.set_amounts() self.clear_unallocated_reference_document_rows() self.validate_payment_against_negative_invoice() @@ -386,6 +389,43 @@ class PaymentEntry(AccountsController): else: self.status = 'Draft' + def set_tax_withholding(self): + if not self.party_type == 'Supplier': + return + + if not self.apply_tax_withholding_amount: + return + + if self.references: + return + + args = frappe._dict({ + 'company': self.company, + 'supplier': self.party, + 'posting_date': self.posting_date, + 'net_total': self.paid_amount + }) + + tax_withholding_details = get_party_tax_withholding_details(args, self.tax_withholding_category) + + 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) + def set_amounts(self): self.set_amounts_in_company_currency() self.set_total_allocated_amount() @@ -423,12 +463,12 @@ class PaymentEntry(AccountsController): if self.payment_type == "Receive" \ and self.base_total_allocated_amount < self.base_received_amount + total_deductions \ and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate): - self.unallocated_amount = (self.base_received_amount + total_deductions - + self.unallocated_amount = (self.base_received_amount + self.base_total_taxes_and_charges + total_deductions - self.base_total_allocated_amount) / self.source_exchange_rate elif self.payment_type == "Pay" \ and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \ and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate): - self.unallocated_amount = (self.base_paid_amount - (total_deductions + + self.unallocated_amount = (self.base_paid_amount + self.base_total_taxes_and_charges - (total_deductions + self.base_total_allocated_amount)) / self.target_exchange_rate def set_difference_amount(self): @@ -446,7 +486,7 @@ class PaymentEntry(AccountsController): total_deductions = sum([flt(d.amount) for d in self.get("deductions")]) - self.difference_amount = flt(self.difference_amount - total_deductions, + self.difference_amount = flt(self.difference_amount - total_deductions + self.base_total_taxes_and_charges, self.precision("difference_amount")) # Paid amount is auto allocated in the reference document by default. @@ -532,6 +572,7 @@ class PaymentEntry(AccountsController): self.add_party_gl_entries(gl_entries) self.add_bank_gl_entries(gl_entries) self.add_deductions_gl_entries(gl_entries) + self.add_tax_gl_entries(gl_entries) make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj) @@ -607,6 +648,26 @@ class PaymentEntry(AccountsController): }, item=self) ) + def add_tax_gl_entries(self, gl_entries): + for d in self.get('taxes'): + account_currency = get_account_currency(d.account_head) + if account_currency != self.company_currency: + frappe.throw(_("Currency for {0} must be {1}").format(d.account_head, self.company_currency)) + + dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit" + + gl_entries.append( + self.get_gl_dict({ + "account": d.account_head, + "against": self.party if self.payment_type=="Receive" else self.paid_from, + dr_or_cr: d.base_tax_amount, + dr_or_cr + "_in_account_currency": d.base_tax_amount \ + if account_currency==self.company_currency \ + else d.tax_amount, + "cost_center": d.cost_center + }, account_currency, item=d) + ) + def add_deductions_gl_entries(self, gl_entries): for d in self.get("deductions"): if d.amount: @@ -671,6 +732,51 @@ class PaymentEntry(AccountsController): self.append('deductions', row) self.set_unallocated_amount() + def calculate_taxes(self): + self.total_taxes_and_charges = 0.0 + self.base_total_taxes_and_charges = 0.0 + + for i, tax in enumerate(self.taxes): + tax_rate = tax.rate + current_tax_rate = 0.0 + + # To set row_id by default as previous row. + if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"]: + if tax.idx == 1: + frappe.throw(_("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")) + + if not tax.row_id: + tax.row_id = tax.idx - 1 + + if tax.charge_type == "Actual": + current_tax_amount = flt(tax.tax_amount, self.precision("tax_amount", tax)) + elif tax.charge_type == "On Paid Amount": + current_tax_amount = (tax_rate / 100.0) * self.paid_amount + elif tax.charge_type == "On Previous Row Amount": + current_tax_amount = (tax_rate / 100.0) * \ + self.taxes[cint(tax.row_id) - 1].tax_amount + + elif tax.charge_type == "On Previous Row Total": + current_tax_amount = (tax_rate / 100.0) * \ + self.taxes[cint(tax.row_id) - 1].total + + tax.tax_amount = current_tax_amount + tax.base_tax_amount = tax.tax_amount * self.source_exchange_rate + + if tax.add_deduct_tax == "Deduct": + current_tax_amount *= -1.0 + else: + current_tax_amount *= 1.0 + + if i == 0: + tax.total = flt(self.paid_amount + current_tax_amount, self.precision("total", tax)) + else: + tax.total = flt(self.taxes[i-1].total + current_tax_amount, self.precision("total", tax)) + + tax.base_total = tax.total * self.source_exchange_rate + self.total_taxes_and_charges += current_tax_amount + self.base_total_taxes_and_charges += current_tax_amount * self.source_exchange_rate + @frappe.whitelist() def get_outstanding_reference_documents(args): diff --git a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json index 7060d11691..61a1462dd7 100644 --- a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json +++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json @@ -1,140 +1,70 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-06-15 15:56:30.815503", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "creation": "2016-06-15 15:56:30.815503", + "doctype": "DocType", + "editable_grid": 1, + "field_order": [ + "account", + "cost_center", + "amount", + "column_break_2", + "description" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 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": 1, - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Account", + "options": "Account", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cost_center", - "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": "Cost Center", - "length": 0, - "no_copy": 0, - "options": "Cost Center", - "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": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "cost_center", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Cost Center", + "options": "Cost Center", + "print_hide": 1, + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "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": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "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": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-01-07 16:52:07.040146", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Payment Entry Deduction", - "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": 0, - "track_seen": 0, - "track_views": 0 + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-09-12 20:38:08.110674", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Payment Entry Deduction", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 544e624725..65e5823b91 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -617,6 +617,10 @@ class AccountsController(TransactionBase): payment_entries = get_advance_payment_entries(party_type, party, party_account, order_doctype, order_list, include_unallocated) + payment_entry_list = [d.reference_name for d in payment_entries] + + payment_entry_taxes = get_payment_entry_taxes(payment_entry_list) + res = journal_entries + payment_entries return res @@ -1241,6 +1245,12 @@ def get_advance_payment_entries(party_type, party, party_account, order_doctype, return list(payment_entries_against_order) + list(unallocated_payment_entries) +def get_payment_entry_taxes(payment_entry_list): + taxes = frappe.db.sql(""" + SELECT t.parent, t.add_deduct_tax, t.charge_type, t.account_head, t.cost_center, + t.tax_amount FROM `tabAdvance Taxes and Charges` where t.parent in %s""" + ,(payment_entry_list, ), as_dict=1) + def update_invoice_status(): # Daily update the status of the invoices diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index ceeecb28a2..23d636061c 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -165,7 +165,7 @@ cur_frm.cscript.validate_taxes_and_charges = function(cdt, cdn) { msg = __("Please select Charge Type first"); d.row_id = ""; d.rate = d.tax_amount = 0.0; - } else if((d.charge_type == 'Actual' || d.charge_type == 'On Net Total') && d.row_id) { + } else if((d.charge_type == 'Actual' || d.charge_type == 'On Net Total' || d.charge_type == 'On Paid Amount') && d.row_id) { msg = __("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'"); d.row_id = ""; } else if((d.charge_type == 'On Previous Row Amount' || d.charge_type == 'On Previous Row Total') && d.row_id) { From c26de286138426f74f4752c6139ce243c5bf854e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 2 Nov 2020 19:57:27 +0530 Subject: [PATCH 042/180] fix: TDS against Purhase Orders --- .../advance_taxes_and_charges.json | 4 +- .../doctype/payment_entry/payment_entry.js | 12 +- .../doctype/payment_entry/payment_entry.json | 31 +- .../doctype/payment_entry/payment_entry.py | 25 +- .../purchase_invoice/purchase_invoice.json | 597 +++++++++++++----- .../purchase_invoice/purchase_invoice.py | 5 +- .../purchase_taxes_and_charges.json | 101 ++- erpnext/controllers/accounts_controller.py | 11 - erpnext/controllers/taxes_and_totals.py | 40 ++ erpnext/public/js/payment/payments.js | 18 +- 10 files changed, 635 insertions(+), 209 deletions(-) diff --git a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json index 79eda8c5fe..5b3299e656 100644 --- a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json +++ b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json @@ -33,7 +33,7 @@ "label": "Type", "oldfieldname": "charge_type", "oldfieldtype": "Select", - "options": "\nActual\nOn Paid Amount\nOn Previous Row Amount\nOn Previous Row Total", + "options": "\nActual\nOn Paid Amount\nIncluded In Paid Amount\nOn Previous Row Amount\nOn Previous Row Total", "reqd": 1, "show_days": 1, "show_seconds": 1 @@ -190,7 +190,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-09-12 22:30:36.150935", + "modified": "2020-10-27 20:02:34.734260", "modified_by": "Administrator", "module": "Accounts", "name": "Advance Taxes and Charges", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 29e4a31dad..3e11bc0e59 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -220,7 +220,7 @@ frappe.ui.form.on('Payment Entry', { var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: ""; frm.set_currency_labels(["base_paid_amount", "base_received_amount", "base_total_allocated_amount", - "difference_amount"], company_currency); + "difference_amount", "base_paid_amount_after_tax"], company_currency); frm.set_currency_labels(["paid_amount"], frm.doc.paid_from_account_currency); frm.set_currency_labels(["received_amount"], frm.doc.paid_to_account_currency); @@ -368,6 +368,16 @@ frappe.ui.form.on('Payment Entry', { } }, + apply_tax_withholding_amount: function(frm) { + if (!frm.doc.apply_tax_withholding_amount) { + frm.set_value("tax_withholding_category", ''); + } else { + frappe.db.get_value('Supplier', frm.doc.party, 'tax_withholding_category', (values) => { + frm.set_value("tax_withholding_category", values.tax_withholding_category); + }); + } + }, + paid_from: function(frm) { if(frm.set_party_account_based_on_party) return; diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 11ae17093f..f158345af6 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -25,9 +25,9 @@ "contact_person", "contact_email", "tds_details_section", - "tax_withholding_category", - "column_break_20", "apply_tax_withholding_amount", + "column_break_20", + "tax_withholding_category", "payment_accounts_section", "party_balance", "paid_from", @@ -39,8 +39,10 @@ "paid_to_account_balance", "payment_amounts_section", "paid_amount", + "paid_amount_after_tax", "source_exchange_rate", "base_paid_amount", + "base_paid_amount_after_tax", "column_break_21", "received_amount", "target_exchange_rate", @@ -580,7 +582,9 @@ "fieldtype": "Small Text", "label": "Remarks", "no_copy": 1, - "read_only_depends_on": "eval:doc.custom_remarks == 0" + "read_only_depends_on": "eval:doc.custom_remarks == 0", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_16", @@ -736,6 +740,7 @@ "show_seconds": 1 }, { + "depends_on": "eval:doc.apply_tax_withholding_amount", "fieldname": "tax_withholding_category", "fieldtype": "Link", "label": "Tax Withholding Category", @@ -808,12 +813,30 @@ "read_only": 1, "show_days": 1, "show_seconds": 1 + }, + { + "fieldname": "paid_amount_after_tax", + "fieldtype": "Currency", + "hidden": 1, + "label": "Paid Amount After Tax", + "options": "paid_from_account_currency", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "base_paid_amount_after_tax", + "fieldtype": "Currency", + "label": "Paid Amount After Tax (Company Currency)", + "options": "Company:company:default_currency", + "show_days": 1, + "show_seconds": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-09-13 22:33:59.860146", + "modified": "2020-10-25 20:50:14.896628", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index a41b4512a1..a21deb5431 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -396,9 +396,6 @@ class PaymentEntry(AccountsController): if not self.apply_tax_withholding_amount: return - if self.references: - return - args = frappe._dict({ 'company': self.company, 'supplier': self.party, @@ -427,11 +424,18 @@ class PaymentEntry(AccountsController): self.remove(d) def set_amounts(self): + self.set_amounts_after_tax() self.set_amounts_in_company_currency() self.set_total_allocated_amount() self.set_unallocated_amount() self.set_difference_amount() + def set_amounts_after_tax(self): + self.paid_amount_after_tax = flt(flt(self.paid_amount) + flt(self.total_taxes_and_charges), + self.precision("paid_amount_after_tax")) + self.base_paid_amount_after_tax = flt(flt(self.paid_amount_after_tax) * flt(self.source_exchange_rate), + self.precision("base_paid_amount_after_tax")) + def set_amounts_in_company_currency(self): self.base_paid_amount, self.base_received_amount, self.difference_amount = 0, 0, 0 if self.paid_amount: @@ -463,12 +467,12 @@ class PaymentEntry(AccountsController): if self.payment_type == "Receive" \ and self.base_total_allocated_amount < self.base_received_amount + total_deductions \ and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate): - self.unallocated_amount = (self.base_received_amount + self.base_total_taxes_and_charges + total_deductions - + self.unallocated_amount = (self.base_received_amount + total_deductions - self.base_total_allocated_amount) / self.source_exchange_rate elif self.payment_type == "Pay" \ and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \ and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate): - self.unallocated_amount = (self.base_paid_amount + self.base_total_taxes_and_charges - (total_deductions + + self.unallocated_amount = (self.base_paid_amount - (total_deductions + self.base_total_allocated_amount)) / self.target_exchange_rate def set_difference_amount(self): @@ -486,7 +490,7 @@ class PaymentEntry(AccountsController): total_deductions = sum([flt(d.amount) for d in self.get("deductions")]) - self.difference_amount = flt(self.difference_amount - total_deductions + self.base_total_taxes_and_charges, + self.difference_amount = flt(self.difference_amount - total_deductions, self.precision("difference_amount")) # Paid amount is auto allocated in the reference document by default. @@ -631,8 +635,8 @@ class PaymentEntry(AccountsController): "account": self.paid_from, "account_currency": self.paid_from_account_currency, "against": self.party if self.payment_type=="Pay" else self.paid_to, - "credit_in_account_currency": self.paid_amount, - "credit": self.base_paid_amount, + "credit_in_account_currency": self.paid_amount_after_tax, + "credit": self.base_paid_amount_after_tax, "cost_center": self.cost_center }, item=self) ) @@ -654,7 +658,10 @@ class PaymentEntry(AccountsController): if account_currency != self.company_currency: frappe.throw(_("Currency for {0} must be {1}").format(d.account_head, self.company_currency)) - dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit" + if self.payment_type == 'Pay': + dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit" + elif self.payment_type == 'Receive': + dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit" gl_entries.append( self.get_gl_dict({ diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index d3d3ffa17f..36450a9255 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -127,6 +127,7 @@ "write_off_cost_center", "advances_section", "allocate_advances_automatically", + "adjust_advance_taxes", "get_advances", "advances", "payment_schedule_section", @@ -175,7 +176,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -187,7 +190,9 @@ "options": "ACC-PINV-.YYYY.-\nACC-PINV-RET-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier", @@ -199,7 +204,9 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -211,7 +218,9 @@ "label": "Supplier Name", "oldfieldname": "supplier_name", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "supplier.tax_id", @@ -219,21 +228,27 @@ "fieldtype": "Read Only", "label": "Tax Id", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "due_date", "fieldtype": "Date", "label": "Due Date", "oldfieldname": "due_date", - "oldfieldtype": "Date" + "oldfieldtype": "Date", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "is_paid", "fieldtype": "Check", "label": "Is Paid", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -241,19 +256,25 @@ "fieldtype": "Check", "label": "Is Return (Debit Note)", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "apply_tds", "fieldtype": "Check", "label": "Apply Tax Withholding Amount", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -263,13 +284,17 @@ "label": "Company", "options": "Company", "print_hide": 1, - "remember_last_selected_value": 1 + "remember_last_selected_value": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cost_center", "fieldtype": "Link", "label": "Cost Center", - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "default": "Today", @@ -281,7 +306,9 @@ "oldfieldtype": "Date", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "posting_time", @@ -290,6 +317,8 @@ "no_copy": 1, "print_hide": 1, "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -298,7 +327,9 @@ "fieldname": "set_posting_time", "fieldtype": "Check", "label": "Edit Posting Date and Time", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -310,44 +341,58 @@ "oldfieldtype": "Link", "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:doc.on_hold", "fieldname": "sb_14", "fieldtype": "Section Break", - "label": "Hold Invoice" + "label": "Hold Invoice", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "on_hold", "fieldtype": "Check", - "label": "Hold Invoice" + "label": "Hold Invoice", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.on_hold", "description": "Once set, this invoice will be on hold till the set date", "fieldname": "release_date", "fieldtype": "Date", - "label": "Release Date" + "label": "Release Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb_17", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.on_hold", "fieldname": "hold_comment", "fieldtype": "Small Text", - "label": "Reason For Putting On Hold" + "label": "Reason For Putting On Hold", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "bill_no", "fieldname": "supplier_invoice_details", "fieldtype": "Section Break", - "label": "Supplier Invoice Details" + "label": "Supplier Invoice Details", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_no", @@ -355,11 +400,15 @@ "label": "Supplier Invoice No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_15", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_date", @@ -368,13 +417,17 @@ "no_copy": 1, "oldfieldname": "bill_date", "oldfieldtype": "Date", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "return_against", "fieldname": "returns", "fieldtype": "Section Break", - "label": "Returns" + "label": "Returns", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "return_against", @@ -384,26 +437,34 @@ "no_copy": 1, "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", @@ -411,51 +472,67 @@ "in_global_search": 1, "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", "fieldtype": "Small Text", "label": "Contact Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -464,7 +541,9 @@ "oldfieldname": "currency", "oldfieldtype": "Select", "options": "Currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "conversion_rate", @@ -473,18 +552,24 @@ "oldfieldname": "conversion_rate", "oldfieldtype": "Currency", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break2", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_currency", @@ -492,14 +577,18 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -508,11 +597,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -521,7 +614,9 @@ "fieldtype": "Link", "label": "Set Accepted Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -531,11 +626,15 @@ "label": "Rejected Warehouse", "no_copy": 1, "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -543,25 +642,33 @@ "fieldtype": "Select", "label": "Raw Materials Supplied", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "update_stock", "fieldtype": "Check", "label": "Update Stock", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -571,42 +678,56 @@ "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Purchase Invoice Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rule_details", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_materials_supplied", "fieldtype": "Section Break", - "label": "Raw Materials Supplied" + "label": "Raw Materials Supplied", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", "fieldtype": "Table", "label": "Supplied Items", "options": "Purchase Receipt Item Supplied", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_26", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -614,7 +735,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -624,18 +747,24 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_28", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -645,42 +774,56 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_49", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_51", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -689,7 +832,9 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -697,13 +842,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -712,13 +861,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -728,7 +881,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -738,7 +893,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -748,11 +905,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_40", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_added", @@ -762,7 +923,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -772,7 +935,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -780,14 +945,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "section_break_44", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -795,7 +964,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -803,28 +974,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_46", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_49", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -834,7 +1015,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -843,7 +1026,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -853,7 +1038,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_in_words", @@ -863,13 +1050,17 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break8", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 1, + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -880,7 +1071,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -889,7 +1082,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -899,7 +1094,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -909,7 +1106,9 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_advance", @@ -920,7 +1119,9 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "outstanding_amount", @@ -931,14 +1132,18 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "depends_on": "grand_total", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -946,20 +1151,26 @@ "depends_on": "eval:doc.is_paid===1||(doc.advances && doc.advances.length>0)", "fieldname": "payments_section", "fieldtype": "Section Break", - "label": "Payments" + "label": "Payments", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "mode_of_payment", "fieldtype": "Link", "label": "Mode of Payment", "options": "Mode of Payment", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cash_bank_account", "fieldtype": "Link", "label": "Cash/Bank Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "clearance_date", @@ -967,11 +1178,15 @@ "label": "Clearance Date", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_br_payments", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_paid", @@ -980,7 +1195,9 @@ "label": "Paid Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_paid_amount", @@ -989,7 +1206,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -997,7 +1216,9 @@ "depends_on": "grand_total", "fieldname": "write_off", "fieldtype": "Section Break", - "label": "Write Off" + "label": "Write Off", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "write_off_amount", @@ -1005,7 +1226,9 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_write_off_amount", @@ -1014,11 +1237,15 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_61", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1026,7 +1253,9 @@ "fieldtype": "Link", "label": "Write Off Account", "options": "Account", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1034,7 +1263,9 @@ "fieldtype": "Link", "label": "Write Off Cost Center", "options": "Cost Center", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1044,13 +1275,17 @@ "label": "Advance Payments", "oldfieldtype": "Section Break", "options": "fa fa-money", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "allocate_advances_automatically", "fieldtype": "Check", - "label": "Set Advances and Allocate (FIFO)" + "label": "Set Advances and Allocate (FIFO)", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.allocate_advances_automatically", @@ -1058,7 +1293,9 @@ "fieldtype": "Button", "label": "Get Advances Paid", "oldfieldtype": "Button", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "advances", @@ -1068,20 +1305,26 @@ "oldfieldname": "advance_allocation_details", "oldfieldtype": "Table", "options": "Purchase Invoice Advance", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:(!doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms" + "label": "Payment Terms", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template" + "options": "Payment Terms Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_schedule", @@ -1089,7 +1332,9 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1097,25 +1342,33 @@ "fieldname": "terms_section_break", "fieldtype": "Section Break", "label": "Terms and Conditions", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", "fieldtype": "Link", "label": "Terms", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", - "label": "Terms and Conditions1" + "label": "Terms and Conditions1", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings" + "label": "Printing Settings", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1123,7 +1376,9 @@ "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1131,11 +1386,15 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_112", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1147,14 +1406,18 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1163,7 +1426,9 @@ "label": "More Information", "oldfieldtype": "Section Break", "options": "fa fa-file-text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "credit_to", @@ -1174,7 +1439,9 @@ "options": "Account", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "party_account_currency", @@ -1184,7 +1451,9 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -1194,7 +1463,9 @@ "oldfieldname": "is_opening", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "against_expense_account", @@ -1204,11 +1475,15 @@ "no_copy": 1, "oldfieldname": "against_expense_account", "oldfieldtype": "Small Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_63", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -1235,14 +1510,18 @@ "no_copy": 1, "oldfieldname": "remarks", "oldfieldtype": "Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", "label": "Subscription Section", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1251,7 +1530,9 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1260,11 +1541,15 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_114", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -1273,24 +1558,32 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference" + "label": "Update Auto Repeat Reference", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", - "label": "Accounting Dimensions " + "label": "Accounting Dimensions ", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "dimension_col_break", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -1298,7 +1591,9 @@ "fieldname": "is_internal_supplier", "fieldtype": "Check", "label": "Is Internal Supplier", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_withholding_category", @@ -1306,19 +1601,25 @@ "hidden": 1, "label": "Tax Withholding Category", "options": "Tax Withholding Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "billing_address", "fieldtype": "Link", "label": "Select Billing Address", - "options": "Address" + "options": "Address", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "billing_address_display", "fieldtype": "Small Text", "label": "Billing Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "project", @@ -1380,7 +1681,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2021-04-30 22:45:58.334107", + "modified": "2020-10-27 21:21:22.647349", "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 83e9f7583e..84dd491a56 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1088,9 +1088,10 @@ class PurchaseInvoice(BuyingController): accounts = [] for d in self.taxes: - if d.account_head == tax_withholding_details.get("account_head"): + if d.account_head == tax_withholding_details.get("account_head") and not d.is_advance_tax: d.update(tax_withholding_details) - accounts.append(d.account_head) + if not d.is_advance_tax: + accounts.append(d.account_head) if not accounts or tax_withholding_details.get("account_head") not in accounts: self.append("taxes", tax_withholding_details) diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json index f9fdc4b605..de7ef2ac96 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json @@ -28,7 +28,8 @@ "base_tax_amount", "base_total", "base_tax_amount_after_discount_amount", - "item_wise_tax_detail" + "item_wise_tax_detail", + "is_advance_tax" ], "fields": [ { @@ -39,7 +40,9 @@ "oldfieldname": "category", "oldfieldtype": "Select", "options": "Valuation and Total\nValuation\nTotal", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "Add", @@ -49,7 +52,9 @@ "oldfieldname": "add_deduct_tax", "oldfieldtype": "Select", "options": "Add\nDeduct", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "columns": 2, @@ -61,7 +66,9 @@ "oldfieldname": "charge_type", "oldfieldtype": "Select", "options": "\nActual\nOn Net Total\nOn Previous Row Amount\nOn Previous Row Total\nOn Item Quantity", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:[\"On Previous Row Amount\", \"On Previous Row Total\"].indexOf(doc.charge_type)!==-1", @@ -69,7 +76,9 @@ "fieldtype": "Data", "label": "Reference Row #", "oldfieldname": "row_id", - "oldfieldtype": "Data" + "oldfieldtype": "Data", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -77,11 +86,15 @@ "fieldname": "included_in_print_rate", "fieldtype": "Check", "label": "Is this Tax included in Basic Rate?", - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break1", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "columns": 2, @@ -92,7 +105,9 @@ "oldfieldname": "account_head", "oldfieldtype": "Link", "options": "Account", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": ":Company", @@ -101,7 +116,9 @@ "label": "Cost Center", "oldfieldname": "cost_center", "oldfieldtype": "Link", - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "description", @@ -111,11 +128,15 @@ "oldfieldtype": "Small Text", "print_width": "300px", "reqd": 1, + "show_days": 1, + "show_seconds": 1, "width": "300px" }, { "fieldname": "section_break_10", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "columns": 2, @@ -124,11 +145,15 @@ "in_list_view": 1, "label": "Rate", "oldfieldname": "rate", - "oldfieldtype": "Currency" + "oldfieldtype": "Currency", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_9", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "columns": 2, @@ -138,7 +163,9 @@ "label": "Amount", "oldfieldname": "tax_amount", "oldfieldtype": "Currency", - "options": "currency" + "options": "currency", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_amount_after_discount_amount", @@ -146,7 +173,9 @@ "label": "Tax Amount After Discount Amount", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "columns": 2, @@ -157,11 +186,15 @@ "oldfieldname": "total", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_14", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_tax_amount", @@ -169,7 +202,9 @@ "label": "Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -177,7 +212,9 @@ "hidden": 1, "label": "Total (Company Currency)", "options": "Company:company:default_currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_tax_amount_after_discount_amount", @@ -185,7 +222,9 @@ "label": "Tax Amount After Discount Amount", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "item_wise_tax_detail", @@ -195,22 +234,38 @@ "oldfieldname": "item_wise_tax_detail", "oldfieldtype": "Small Text", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", - "label": "Accounting Dimensions" + "label": "Accounting Dimensions", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "dimension_col_break", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "fieldname": "is_advance_tax", + "fieldtype": "Check", + "hidden": 1, + "label": "Is Advance Tax", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-09-18 17:26:09.703215", + "modified": "2020-10-25 18:24:43.487567", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Taxes and Charges", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 65e5823b91..a805dc4e91 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -617,10 +617,6 @@ class AccountsController(TransactionBase): payment_entries = get_advance_payment_entries(party_type, party, party_account, order_doctype, order_list, include_unallocated) - payment_entry_list = [d.reference_name for d in payment_entries] - - payment_entry_taxes = get_payment_entry_taxes(payment_entry_list) - res = journal_entries + payment_entries return res @@ -1245,13 +1241,6 @@ def get_advance_payment_entries(party_type, party, party_account, order_doctype, return list(payment_entries_against_order) + list(unallocated_payment_entries) -def get_payment_entry_taxes(payment_entry_list): - taxes = frappe.db.sql(""" - SELECT t.parent, t.add_deduct_tax, t.charge_type, t.account_head, t.cost_center, - t.tax_amount FROM `tabAdvance Taxes and Charges` where t.parent in %s""" - ,(payment_entry_list, ), as_dict=1) - - def update_invoice_status(): # Daily update the status of the invoices diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 9fae49482d..f3d7b2635e 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -40,6 +40,7 @@ class calculate_taxes_and_totals(object): self.validate_conversion_rate() self.calculate_item_values() self.validate_item_tax_template() + self.apply_advance_taxes() self.initialize_taxes() self.determine_exclusive_rate() self.calculate_net_total() @@ -683,6 +684,32 @@ class calculate_taxes_and_totals(object): self.calculate_paid_amount() + def apply_advance_taxes(self): + if cint(self.doc.get('adjust_advance_taxes')): + if self.doc.get('advances'): + payment_entry_list = [d.reference_name for d in self.doc.get('advances')] + advance_taxes = get_advance_taxes(payment_entry_list) + accounts = [] + + # Remove already added advance taxes if any + for tax in self.doc.get('taxes'): + if tax.is_advance_tax: + self.doc.remove(tax) + else: + accounts.append(tax.account_head) + + for tax in advance_taxes: + # Reverse add deduct from payment entry in invoice + if tax.account_head in accounts: + add_deduct_tax = 'Deduct' if tax.add_deduct_tax == 'Add' else 'Add' + tax.update({ + 'add_deduct_tax': add_deduct_tax, + 'category': tax.get('category') or 'Total', + 'is_advance_tax': 1, + 'charge_type': 'On Net Total' if tax.charge_type == 'On Paid Amount' else tax.charge_type + }) + + self.doc.append('taxes', tax) def get_itemised_tax_breakup_html(doc): if not doc.taxes: @@ -820,3 +847,16 @@ class init_landed_taxes_and_totals(object): for d in self.doc.get(self.tax_field): d.amount = flt(d.amount, d.precision("amount")) d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount")) + +def get_advance_taxes(payment_entry_list): + taxes = [] + if payment_entry_list: + taxes = frappe.db.sql( + """ + SELECT t.parent, t.add_deduct_tax, t.charge_type, t.rate, + t.account_head, t.cost_center, t.tax_amount, t.description + FROM `tabAdvance Taxes and Charges` t, `tabPayment Entry` p + WHERE t.parent = p.name AND t.parent in %s + """, (payment_entry_list, ), as_dict=1) + + return taxes diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js index 0d656bc1fb..b168952e40 100644 --- a/erpnext/public/js/payment/payments.js +++ b/erpnext/public/js/payment/payments.js @@ -8,7 +8,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.dialog = new frappe.ui.Dialog({ title: 'Payment' }); - + this.dialog.show(); this.$body = this.dialog.body; this.set_payment_primary_action(); @@ -25,7 +25,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ 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) { @@ -100,7 +100,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.selected_mode.select() this.bind_amount_change_event(); }, - + bind_keyboard_event: function(){ var me = this; this.payment_val = ''; @@ -114,17 +114,17 @@ erpnext.payments = erpnext.stock.StockController.extend({ 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")); }) @@ -138,7 +138,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ $(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(){ @@ -147,7 +147,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ 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)); @@ -156,7 +156,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ }) }, - + bind_amount_change_event: function(){ var me = this; this.selected_mode.change(function(){ From d18dde77577cb041ce06e4cf7971087157ceb933 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 29 Nov 2020 21:40:04 +0530 Subject: [PATCH 043/180] fix: Add tds in PO and code cleanup --- .../advance_taxes_and_charges.json | 31 +- .../doctype/payment_entry/payment_entry.json | 66 ++- .../doctype/payment_entry/payment_entry.py | 89 +++- .../purchase_invoice/purchase_invoice.py | 48 +- .../purchase_taxes_and_charges.json | 15 +- .../doctype/sales_invoice/sales_invoice.py | 42 ++ .../doctype/purchase_order/purchase_order.js | 8 + .../purchase_order/purchase_order.json | 481 +++++++++++++----- .../doctype/purchase_order/purchase_order.py | 35 ++ erpnext/controllers/accounts_controller.py | 30 ++ erpnext/controllers/taxes_and_totals.py | 28 - 11 files changed, 676 insertions(+), 197 deletions(-) diff --git a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json index 5b3299e656..aac7eb6bcb 100644 --- a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json +++ b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json @@ -12,6 +12,7 @@ "account_head", "col_break_1", "description", + "included_in_paid_amount", "accounting_dimensions_section", "cost_center", "dimension_col_break", @@ -20,9 +21,11 @@ "section_break_9", "tax_amount", "total", + "allocated_amount", "column_break_13", "base_tax_amount", - "base_total" + "base_total", + "base_allocated_amount" ], "fields": [ { @@ -185,12 +188,36 @@ "reqd": 1, "show_days": 1, "show_seconds": 1 + }, + { + "default": "0", + "fieldname": "included_in_paid_amount", + "fieldtype": "Check", + "label": "Included In Paid Amount", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "allocated_amount", + "fieldtype": "Currency", + "label": "Allocated Amount", + "options": "currency", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "base_allocated_amount", + "fieldtype": "Currency", + "label": "Allocated Amount (Company Currency)", + "options": "Company:company:default_currency", + "show_days": 1, + "show_seconds": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-10-27 20:02:34.734260", + "modified": "2020-11-29 19:06:14.666460", "modified_by": "Administrator", "module": "Accounts", "name": "Advance Taxes and Charges", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index f158345af6..ec92db0708 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -24,15 +24,12 @@ "party_bank_account", "contact_person", "contact_email", - "tds_details_section", - "apply_tax_withholding_amount", - "column_break_20", - "tax_withholding_category", "payment_accounts_section", "party_balance", "paid_from", "paid_from_account_currency", "paid_from_account_balance", + "advance_tax_account", "column_break_18", "paid_to", "paid_to_account_currency", @@ -45,8 +42,10 @@ "base_paid_amount_after_tax", "column_break_21", "received_amount", + "received_amount_after_tax", "target_exchange_rate", "base_received_amount", + "base_received_amount_after_tax", "section_break_14", "get_outstanding_invoice", "references", @@ -61,6 +60,10 @@ "taxes_and_charges_section", "purchase_taxes_and_charges_template", "sales_taxes_and_charges_template", + "column_break_55", + "apply_tax_withholding_amount", + "tax_withholding_category", + "section_break_56", "taxes", "base_total_taxes_and_charges", "total_taxes_and_charges", @@ -731,14 +734,6 @@ "fieldtype": "Check", "label": "Custom Remarks" }, - { - "depends_on": "eval: doc.payment_type == 'Pay' && doc.party_type == 'Supplier'", - "fieldname": "tds_details_section", - "fieldtype": "Section Break", - "label": "TDS Details", - "show_days": 1, - "show_seconds": 1 - }, { "depends_on": "eval:doc.apply_tax_withholding_amount", "fieldname": "tax_withholding_category", @@ -750,18 +745,13 @@ }, { "default": "0", + "depends_on": "eval:doc.party_type == 'Supplier'", "fieldname": "apply_tax_withholding_amount", "fieldtype": "Check", "label": "Apply Tax Withholding Amount", "show_days": 1, "show_seconds": 1 }, - { - "fieldname": "column_break_20", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 - }, { "collapsible": 1, "fieldname": "taxes_and_charges_section", @@ -829,6 +819,44 @@ "fieldtype": "Currency", "label": "Paid Amount After Tax (Company Currency)", "options": "Company:company:default_currency", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "column_break_55", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "section_break_56", + "fieldtype": "Section Break", + "hide_border": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "advance_tax_account", + "fieldtype": "Link", + "label": "Advance Tax Account", + "options": "Account", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "received_amount_after_tax", + "fieldtype": "Currency", + "label": "Received Amount After Tax", + "options": "paid_to_account_currency", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "base_received_amount_after_tax", + "fieldtype": "Currency", + "label": "Received Amount After Tax (Company Currency)", + "options": "Company:company:default_currency", "show_days": 1, "show_seconds": 1 } @@ -836,7 +864,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-10-25 20:50:14.896628", + "modified": "2020-11-29 20:03:57.772062", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index a21deb5431..251b581723 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -65,6 +65,7 @@ class PaymentEntry(AccountsController): self.validate_allocated_amount() self.validate_paid_invoices() self.ensure_supplier_is_not_blocked() + self.validate_advance_tax_account() self.set_status() def on_submit(self): @@ -310,6 +311,9 @@ class PaymentEntry(AccountsController): + "

" + _("If this is undesirable please cancel the corresponding Payment Entry."), title=_("Warning"), indicator="orange") + def validate_advance_tax_account(self): + if self.get('taxes') and not self.advance_tax_account: + frappe.throw(_('Please select advance tax account')) def validate_journal_entry(self): for d in self.get("references"): @@ -396,11 +400,29 @@ class PaymentEntry(AccountsController): if not self.apply_tax_withholding_amount: return + reference_doclist = [] + net_total = self.paid_amount + included_in_paid_amount = 0 + + if self.get('references'): + for doc in self.get('references'): + if doc.reference_doctype == 'Purchase Order': + reference_doclist.append(doc.reference_name) + + if reference_doclist: + order_amount = frappe.db.get_all('Purchase Order', fields=['sum(net_total)'], + filters = {'name': ('in', reference_doclist), 'docstatus': 1, + 'apply_tds': 1}, as_list=1) + + if order_amount: + net_total = order_amount[0][0] + included_in_paid_amount = 1 + args = frappe._dict({ 'company': self.company, 'supplier': self.party, 'posting_date': self.posting_date, - 'net_total': self.paid_amount + 'net_total': net_total }) tax_withholding_details = get_party_tax_withholding_details(args, self.tax_withholding_category) @@ -408,6 +430,8 @@ class PaymentEntry(AccountsController): if not tax_withholding_details: return + tax_withholding_details.update({'included_in_paid_amount': included_in_paid_amount}) + accounts = [] for d in self.taxes: if d.account_head == tax_withholding_details.get("account_head"): @@ -424,18 +448,37 @@ class PaymentEntry(AccountsController): self.remove(d) def set_amounts(self): - self.set_amounts_after_tax() + self.set_received_amount() self.set_amounts_in_company_currency() + self.set_amounts_after_tax() self.set_total_allocated_amount() self.set_unallocated_amount() self.set_difference_amount() + def set_received_amount(self): + self.base_received_amount = self.base_paid_amount + def set_amounts_after_tax(self): - self.paid_amount_after_tax = flt(flt(self.paid_amount) + flt(self.total_taxes_and_charges), + applicable_tax = 0 + base_applicable_tax = 0 + for tax in self.get('taxes'): + if not tax.included_in_paid_amount: + amount = -1 * tax.tax_amount if tax.add_deduct_tax == 'Deduct' else tax.tax_amount + base_amount = -1 * tax.base_tax_amount if tax.add_deduct_tax == 'Deduct' else tax.base_tax_amount + + applicable_tax += amount + base_applicable_tax += base_amount + + self.paid_amount_after_tax = flt(flt(self.paid_amount) + flt(applicable_tax), self.precision("paid_amount_after_tax")) self.base_paid_amount_after_tax = flt(flt(self.paid_amount_after_tax) * flt(self.source_exchange_rate), self.precision("base_paid_amount_after_tax")) + self.received_amount_after_tax = flt(flt(self.received_amount) + flt(applicable_tax), + self.precision("paid_amount_after_tax")) + self.base_received_amount_after_tax = flt(flt(self.received_amount_after_tax) * flt(self.source_exchange_rate), + self.precision("base_paid_amount_after_tax")) + def set_amounts_in_company_currency(self): self.base_paid_amount, self.base_received_amount, self.difference_amount = 0, 0, 0 if self.paid_amount: @@ -465,14 +508,14 @@ class PaymentEntry(AccountsController): if self.party: total_deductions = sum([flt(d.amount) for d in self.get("deductions")]) if self.payment_type == "Receive" \ - and self.base_total_allocated_amount < self.base_received_amount + total_deductions \ - and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate): - self.unallocated_amount = (self.base_received_amount + total_deductions - + and self.base_total_allocated_amount < self.base_received_amount_after_tax + total_deductions \ + and self.total_allocated_amount < self.paid_amount_after_tax + (total_deductions / self.source_exchange_rate): + self.unallocated_amount = (self.received_amount_after_tax + total_deductions - self.base_total_allocated_amount) / self.source_exchange_rate elif self.payment_type == "Pay" \ - and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \ - and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate): - self.unallocated_amount = (self.base_paid_amount - (total_deductions + + and self.base_total_allocated_amount < (self.base_paid_amount_after_tax - total_deductions) \ + and self.total_allocated_amount < self.received_amount_after_tax + (total_deductions / self.target_exchange_rate): + self.unallocated_amount = (self.base_paid_amount_after_tax - (total_deductions + self.base_total_allocated_amount)) / self.target_exchange_rate def set_difference_amount(self): @@ -482,11 +525,11 @@ class PaymentEntry(AccountsController): base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount) if self.payment_type == "Receive": - self.difference_amount = base_party_amount - self.base_received_amount + self.difference_amount = base_party_amount - self.base_received_amount_after_tax elif self.payment_type == "Pay": - self.difference_amount = self.base_paid_amount - base_party_amount + self.difference_amount = self.base_paid_amount_after_tax - base_party_amount else: - self.difference_amount = self.base_paid_amount - flt(self.base_received_amount) + self.difference_amount = self.base_paid_amount_after_tax - flt(self.base_received_amount_after_tax) total_deductions = sum([flt(d.amount) for d in self.get("deductions")]) @@ -616,7 +659,7 @@ class PaymentEntry(AccountsController): gl_entries.append(gle) if self.unallocated_amount: - base_unallocated_amount = base_unallocated_amount = self.unallocated_amount * \ + base_unallocated_amount = self.unallocated_amount * \ (self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate) gle = party_gl_dict.copy() @@ -646,8 +689,8 @@ class PaymentEntry(AccountsController): "account": self.paid_to, "account_currency": self.paid_to_account_currency, "against": self.party if self.payment_type=="Receive" else self.paid_from, - "debit_in_account_currency": self.received_amount, - "debit": self.base_received_amount, + "debit_in_account_currency": self.received_amount_after_tax, + "debit": self.base_received_amount_after_tax, "cost_center": self.cost_center }, item=self) ) @@ -660,8 +703,10 @@ class PaymentEntry(AccountsController): if self.payment_type == 'Pay': dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit" + rev_dr_cr = "credit" if d.add_deduct_tax == "Add" else "debit" elif self.payment_type == 'Receive': dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit" + rev_dr_cr = "debit" if d.add_deduct_tax == "Add" else "credit" gl_entries.append( self.get_gl_dict({ @@ -672,8 +717,18 @@ class PaymentEntry(AccountsController): if account_currency==self.company_currency \ else d.tax_amount, "cost_center": d.cost_center - }, account_currency, item=d) - ) + }, account_currency, item=d)) + + gl_entries.append( + self.get_gl_dict({ + "account": self.advance_tax_account, + "against": self.party if self.payment_type=="Receive" else self.paid_from, + rev_dr_cr: d.base_tax_amount, + rev_dr_cr + "_in_account_currency": d.base_tax_amount \ + if account_currency==self.company_currency \ + else d.tax_amount, + "cost_center": d.cost_center + }, account_currency, item=d)) def add_deductions_gl_entries(self, gl_entries): for d in self.get("deductions"): diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 84dd491a56..8f7248f1e6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -456,6 +456,8 @@ class PurchaseInvoice(BuyingController): self.make_tax_gl_entries(gl_entries) self.make_internal_transfer_gl_entries(gl_entries) + self.allocate_advance_taxes(gl_entries) + gl_entries = make_regional_gl_entries(gl_entries, self) gl_entries = merge_similar_entries(gl_entries) @@ -899,6 +901,46 @@ class PurchaseInvoice(BuyingController): "cost_center": self.cost_center }, account_currency, item=self)) + def allocate_advance_taxes(self, gl_entries): + tax_map = self.get_tax_map() + for pe in self.get('advances'): + pe = frappe.get_doc('Payment Entry', pe.reference_name) + for tax in pe.get('taxes'): + account_currency = get_account_currency(tax.account_head) + dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" + rev_dr_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" + + unallocated_amount = tax.tax_amount - tax.allocated_amount + if tax_map.get(tax.account_head): + amount = tax_map.get(tax.account_head) + if amount < unallocated_amount: + unallocated_amount = amount + + gl_entries.append( + self.get_gl_dict({ + "account": tax.account_head, + "against": self.supplier, + dr_or_cr: unallocated_amount, + dr_or_cr + "_in_account_currency": unallocated_amount \ + if account_currency==self.company_currency \ + else unallocated_amount, + 'cost_center': tax.cost_center + }, account_currency, item=tax)) + + gl_entries.append( + self.get_gl_dict({ + "account": pe.advance_tax_account, + "against": self.supplier, + rev_dr_cr: unallocated_amount, + rev_dr_cr + "_in_account_currency": unallocated_amount \ + if account_currency==self.company_currency \ + else unallocated_amount, + 'cost_center': tax.cost_center + }, account_currency, item=tax)) + + frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount + unallocated_amount) + tax_map[tax.account_head] -= unallocated_amount + def make_payment_gl_entries(self, gl_entries): # Make Cash GL Entries if cint(self.is_paid) and self.cash_bank_account and self.paid_amount: @@ -1088,10 +1130,10 @@ class PurchaseInvoice(BuyingController): accounts = [] for d in self.taxes: - if d.account_head == tax_withholding_details.get("account_head") and not d.is_advance_tax: + if d.account_head == tax_withholding_details.get("account_head"): d.update(tax_withholding_details) - if not d.is_advance_tax: - accounts.append(d.account_head) + + accounts.append(d.account_head) if not accounts or tax_withholding_details.get("account_head") not in accounts: self.append("taxes", tax_withholding_details) diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json index de7ef2ac96..0db0b6823d 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json @@ -28,8 +28,7 @@ "base_tax_amount", "base_total", "base_tax_amount_after_discount_amount", - "item_wise_tax_detail", - "is_advance_tax" + "item_wise_tax_detail" ], "fields": [ { @@ -250,22 +249,12 @@ "fieldtype": "Column Break", "show_days": 1, "show_seconds": 1 - }, - { - "default": "0", - "fieldname": "is_advance_tax", - "fieldtype": "Check", - "hidden": 1, - "label": "Is Advance Tax", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-10-25 18:24:43.487567", + "modified": "2020-11-29 19:11:58.826078", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Taxes and Charges", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index a008742390..9af94fd780 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -842,6 +842,8 @@ class SalesInvoice(SellingController): self.make_tax_gl_entries(gl_entries) self.make_internal_transfer_gl_entries(gl_entries) + self.allocate_advance_taxes(gl_entries) + self.make_item_gl_entries(gl_entries) # merge gl entries before adding pos entries @@ -911,6 +913,46 @@ class SalesInvoice(SellingController): "cost_center": self.cost_center }, account_currency, item=self)) + def allocate_advance_taxes(self, gl_entries): + tax_map = self.get_tax_map() + for pe in self.get('advances'): + pe = frappe.get_doc('Payment Entry', pe.reference_name) + for tax in pe.get('taxes'): + account_currency = get_account_currency(tax.account_head) + dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" + rev_dr_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" + + unallocated_amount = tax.tax_amount - tax.allocated_amount + if tax_map.get(tax.account_head): + amount = tax_map.get(tax.account_head) + if amount < unallocated_amount: + unallocated_amount = amount + + gl_entries.append( + self.get_gl_dict({ + "account": tax.account_head, + "against": self.customer, + dr_or_cr: unallocated_amount, + dr_or_cr + "_in_account_currency": unallocated_amount \ + if account_currency==self.company_currency \ + else unallocated_amount, + 'cost_center': tax.cost_center + }, account_currency, item=tax)) + + gl_entries.append( + self.get_gl_dict({ + "account": pe.advance_tax_account, + "against": self.customer, + rev_dr_cr: unallocated_amount, + rev_dr_cr + "_in_account_currency": unallocated_amount \ + if account_currency==self.company_currency \ + else unallocated_amount, + 'cost_center': tax.cost_center + }, account_currency, item=tax)) + + frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount + unallocated_amount) + tax_map[tax.account_head] -= unallocated_amount + def make_item_gl_entries(self, gl_entries): # income account gl entries for item in self.get("items"): diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index dd0f065848..139aa72c08 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -45,6 +45,14 @@ frappe.ui.form.on("Purchase Order", { }); erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); + }, + + apply_tds: function(frm) { + if (!frm.doc.apply_tds) { + frm.set_value("tax_withholding_category", ''); + } else { + frm.set_value("tax_withholding_category", frm.supplier_tds); + } } }); diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index ee2beea67f..64eda5f280 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -14,6 +14,8 @@ "supplier", "get_items_from_open_material_requests", "supplier_name", + "apply_tds", + "tax_withholding_category", "column_break1", "company", "transaction_date", @@ -142,7 +144,9 @@ { "fieldname": "supplier_section", "fieldtype": "Section Break", - "options": "fa fa-user" + "options": "fa fa-user", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -152,7 +156,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -164,7 +170,9 @@ "options": "PUR-ORD-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -178,14 +186,18 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))", "description": "Fetch items based on Default Supplier.", "fieldname": "get_items_from_open_material_requests", "fieldtype": "Button", - "label": "Get Items from Open Material Requests" + "label": "Get Items from Open Material Requests", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -194,7 +206,9 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Supplier Name", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -206,13 +220,17 @@ "options": "Company", "print_hide": 1, "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -224,27 +242,35 @@ "oldfieldname": "transaction_date", "oldfieldtype": "Date", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "fieldname": "schedule_date", "fieldtype": "Date", - "label": "Required By" + "label": "Required By", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval:doc.docstatus===1", "fieldname": "order_confirmation_no", "fieldtype": "Data", - "label": "Order Confirmation No" + "label": "Order Confirmation No", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval:doc.order_confirmation_no", "fieldname": "order_confirmation_date", "fieldtype": "Date", - "label": "Order Confirmation Date" + "label": "Order Confirmation Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -256,19 +282,25 @@ "oldfieldtype": "Data", "options": "Purchase Order", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "drop_ship", "fieldtype": "Section Break", - "label": "Drop Ship" + "label": "Drop Ship", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer", "fieldtype": "Link", "label": "Customer", "options": "Customer", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -276,31 +308,41 @@ "fieldtype": "Data", "label": "Customer Name", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_19", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_person", "fieldtype": "Link", "label": "Customer Contact", - "options": "Contact" + "options": "Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_display", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_mobile", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Mobile No", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_email", @@ -308,27 +350,35 @@ "hidden": 1, "label": "Customer Contact Email", "options": "Email", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", "label": "Supplier Contact", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", @@ -355,32 +405,42 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Company Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address Details", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -390,7 +450,9 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "conversion_rate", @@ -400,18 +462,24 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb_price_list", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_currency", @@ -419,14 +487,18 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -435,7 +507,9 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", @@ -448,11 +522,15 @@ "fieldtype": "Link", "label": "Set Target Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -461,26 +539,34 @@ "in_standard_filter": 1, "label": "Supply Raw Materials", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", "fieldname": "supplier_warehouse", "fieldtype": "Link", "label": "Supplier Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "items_section", "fieldtype": "Section Break", "hide_border": 1, "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -490,26 +576,34 @@ "oldfieldname": "po_details", "oldfieldtype": "Table", "options": "Purchase Order Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_break_48", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Purchase Order Pricing Rule", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_material_details", "fieldtype": "Section Break", - "label": "Raw Materials Supplied" + "label": "Raw Materials Supplied", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", @@ -519,17 +613,23 @@ "oldfieldtype": "Table", "options": "Purchase Order Item Supplied", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sb_last_purchase", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -537,7 +637,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -548,18 +650,24 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_26", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -569,20 +677,26 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -591,18 +705,24 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_50", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_52", @@ -615,13 +735,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -630,14 +754,18 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "label": "Taxes and Charges", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "base_taxes_and_charges_added", @@ -648,7 +776,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "base_taxes_and_charges_deducted", @@ -659,7 +789,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "base_total_taxes_and_charges", @@ -671,11 +803,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_39", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "taxes_and_charges_added", @@ -686,7 +822,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "taxes_and_charges_deducted", @@ -697,7 +835,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "total_taxes_and_charges", @@ -706,14 +846,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "apply_discount_on", "fieldname": "discount_section", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -721,7 +865,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -729,24 +875,32 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_45", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals_section", @@ -762,7 +916,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -771,7 +927,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "In Words will be visible once you save the Purchase Order.", @@ -782,7 +940,9 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounded_total", @@ -792,12 +952,16 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break4", "fieldtype": "Column Break", - "oldfieldtype": "Column Break" + "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "grand_total", @@ -807,7 +971,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -816,20 +982,26 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounded_total", "fieldtype": "Currency", "label": "Rounded Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -839,7 +1011,9 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "advance_paid", @@ -848,19 +1022,25 @@ "no_copy": 1, "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms" + "label": "Payment Terms", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template" + "options": "Payment Terms Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_schedule", @@ -868,7 +1048,9 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -877,7 +1059,9 @@ "fieldtype": "Section Break", "label": "Terms and Conditions", "oldfieldtype": "Section Break", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", @@ -886,21 +1070,27 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", "label": "Terms and Conditions", "oldfieldname": "terms", - "oldfieldtype": "Text Editor" + "oldfieldtype": "Text Editor", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", "label": "More Information", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -915,7 +1105,9 @@ "print_hide": 1, "read_only": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "ref_sq", @@ -926,7 +1118,9 @@ "oldfieldtype": "Data", "options": "Supplier Quotation", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "party_account_currency", @@ -936,18 +1130,24 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_order_reference", "fieldtype": "Link", "label": "Inter Company Order Reference", "options": "Sales Order", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_74", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -957,7 +1157,9 @@ "label": "% Received", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -967,7 +1169,9 @@ "label": "% Billed", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -977,6 +1181,8 @@ "oldfieldtype": "Column Break", "print_hide": 1, "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -987,7 +1193,9 @@ "oldfieldname": "letter_head", "oldfieldtype": "Select", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -999,11 +1207,15 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_86", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1011,19 +1223,25 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section" + "label": "Subscription Section", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1031,7 +1249,9 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1039,11 +1259,15 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_97", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -1052,27 +1276,35 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference" + "label": "Update Auto Repeat Reference", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", - "options": "Tax Category" + "options": "Tax Category", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "supplied_items", "fieldname": "set_reserve_warehouse", "fieldtype": "Link", "label": "Set Reserve Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1082,7 +1314,9 @@ }, { "fieldname": "column_break_75", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "billing_address", @@ -1118,13 +1352,30 @@ "label": "Represents Company", "options": "Company", "read_only": 1 + }, + { + "default": "0", + "fieldname": "apply_tds", + "fieldtype": "Check", + "label": "Apply Tax Withholding Amount", + "show_days": 1, + "show_seconds": 1 + }, + { + "depends_on": "eval: doc.apply_tds", + "fieldname": "tax_withholding_category", + "fieldtype": "Link", + "label": "Tax Withholding Category", + "options": "Tax Withholding Category", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2021-01-20 22:07:23.487138", + "modified": "2020-11-28 17:42:25.177827", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index ef9372eeb6..782593a5c5 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -17,6 +17,7 @@ from erpnext.accounts.party import get_party_account_currency from six import string_types from erpnext.stock.doctype.item.item import get_item_defaults from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults +from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\ unlink_inter_company_doc @@ -39,11 +40,18 @@ class PurchaseOrder(BuyingController): 'percent_join_field': 'material_request' }] + def onload(self): + supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category") + self.set_onload("supplier_tds", supplier_tds) + def validate(self): super(PurchaseOrder, self).validate() self.set_status() + # apply tax withholding only if checked and applicable + self.set_tax_withholding() + self.validate_supplier() self.validate_schedule_date() validate_for_items(self) @@ -87,6 +95,33 @@ class PurchaseOrder(BuyingController): if cint(frappe.db.get_single_value('Buying Settings', 'maintain_same_rate')): self.validate_rate_with_reference_doc([["Supplier Quotation", "supplier_quotation", "supplier_quotation_item"]]) + def set_tax_withholding(self): + if not self.apply_tds: + return + + tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category) + + 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 validate_supplier(self): prevent_po = frappe.db.get_value("Supplier", self.supplier, 'prevent_pos') if prevent_po: diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a805dc4e91..da88853a96 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -700,6 +700,7 @@ class AccountsController(TransactionBase): from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries if self.doctype in ["Sales Invoice", "Purchase Invoice"]: + self.update_allocated_advance_taxes() if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'): unlink_ref_doc_from_payment_entries(self) @@ -707,6 +708,35 @@ class AccountsController(TransactionBase): if frappe.db.get_single_value('Accounts Settings', 'unlink_advance_payment_on_cancelation_of_order'): unlink_ref_doc_from_payment_entries(self) + def get_tax_map(self): + tax_map = {} + for tax in self.get('taxes'): + tax_map.setdefault(tax.account_head, 0.0) + tax_map[tax.account_head] += tax.tax_amount + + return tax_map + + def update_allocated_advance_taxes(self): + if self.get('advances'): + tax_accounts = [d.account_head for d in self.get('taxes')] + allocated_tax_map = frappe._dict(frappe.get_all('GL Entry', fields=['account', 'sum(credit - debit)'], + filters={'voucher_no': self.name, 'account': ('in', tax_accounts)}, + group_by='account', as_list=1)) + + tax_map = self.get_tax_map() + + for pe in self.get('advances'): + pe = frappe.get_doc('Payment Entry', pe.reference_name) + for tax in pe.get('taxes'): + allocated_amount = tax_map.get(tax.account_head) - allocated_tax_map.get(tax.account_head) + if allocated_amount > tax.tax_amount: + allocated_amount = tax.tax_amount + + if allocated_amount: + frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount - allocated_amount) + tax_map[tax.account_head] -= allocated_amount + allocated_tax_map[tax.account_head] -= allocated_amount + def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield): from erpnext.controllers.status_updater import get_allowance_for item_allowance = {} diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index f3d7b2635e..a6b54eb590 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -40,7 +40,6 @@ class calculate_taxes_and_totals(object): self.validate_conversion_rate() self.calculate_item_values() self.validate_item_tax_template() - self.apply_advance_taxes() self.initialize_taxes() self.determine_exclusive_rate() self.calculate_net_total() @@ -684,33 +683,6 @@ class calculate_taxes_and_totals(object): self.calculate_paid_amount() - def apply_advance_taxes(self): - if cint(self.doc.get('adjust_advance_taxes')): - if self.doc.get('advances'): - payment_entry_list = [d.reference_name for d in self.doc.get('advances')] - advance_taxes = get_advance_taxes(payment_entry_list) - accounts = [] - - # Remove already added advance taxes if any - for tax in self.doc.get('taxes'): - if tax.is_advance_tax: - self.doc.remove(tax) - else: - accounts.append(tax.account_head) - - for tax in advance_taxes: - # Reverse add deduct from payment entry in invoice - if tax.account_head in accounts: - add_deduct_tax = 'Deduct' if tax.add_deduct_tax == 'Add' else 'Add' - tax.update({ - 'add_deduct_tax': add_deduct_tax, - 'category': tax.get('category') or 'Total', - 'is_advance_tax': 1, - 'charge_type': 'On Net Total' if tax.charge_type == 'On Paid Amount' else tax.charge_type - }) - - self.doc.append('taxes', tax) - def get_itemised_tax_breakup_html(doc): if not doc.taxes: return From 79b422c0a96f425b4098516b331b5a96d17c5aec Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 28 Feb 2021 18:07:21 +0530 Subject: [PATCH 044/180] fix: Advance TDS in TDS payable monthly report --- .../doctype/payment_entry/payment_entry.js | 19 ++- .../doctype/payment_entry/payment_entry.json | 2 +- .../doctype/payment_entry/payment_entry.py | 26 ++-- .../purchase_invoice/purchase_invoice.py | 2 +- .../tax_withholding_category.py | 6 +- .../tds_payable_monthly.js | 41 +++++- .../tds_payable_monthly.py | 127 ++++++++++++++---- .../purchase_order/purchase_order.json | 2 +- erpnext/setup/doctype/company/company.js | 2 +- 9 files changed, 175 insertions(+), 52 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 3e11bc0e59..19d73b1ad4 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -93,6 +93,15 @@ frappe.ui.form.on('Payment Entry', { } }); + frm.set_query("advance_tax_account", function() { + return { + filters: { + "company": frm.doc.company, + "root_type": ["in", ["Asset", "Liability"]] + } + } + }); + frm.set_query("reference_doctype", "references", function() { if (frm.doc.party_type == "Customer") { var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"]; @@ -1103,10 +1112,12 @@ frappe.ui.form.on('Payment Entry', { current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; - if(i==0) { - tax.total = flt(frm.doc.paid_amount + current_tax_amount, precision("total", tax)); - } else { - tax.total = flt(frm.doc["taxes"][i-1].total + current_tax_amount, precision("total", tax)); + if (!tax.included_in_paid_amount) { + if(i==0) { + tax.total = flt(frm.doc.paid_amount + current_tax_amount, precision("total", tax)); + } else { + tax.total = flt(frm.doc["taxes"][i-1].total + current_tax_amount, precision("total", tax)); + } } tax.base_total = tax.total * frm.doc.source_exchange_rate; diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index ec92db0708..d239c41fd8 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -864,7 +864,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-11-29 20:03:57.772062", + "modified": "2021-02-27 13:56:20.007336", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 251b581723..33eb6f2f26 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -65,7 +65,7 @@ class PaymentEntry(AccountsController): self.validate_allocated_amount() self.validate_paid_invoices() self.ensure_supplier_is_not_blocked() - self.validate_advance_tax_account() + self.set_advance_tax_account() self.set_status() def on_submit(self): @@ -311,9 +311,13 @@ class PaymentEntry(AccountsController): + "

" + _("If this is undesirable please cancel the corresponding Payment Entry."), title=_("Warning"), indicator="orange") - def validate_advance_tax_account(self): + def set_advance_tax_account(self): if self.get('taxes') and not self.advance_tax_account: - frappe.throw(_('Please select advance tax account')) + unrealized_profit_loss_account = frappe.db.get_value('Company', self.company, 'unrealized_profit_loss_account') + if not unrealized_profit_loss_account: + frappe.throw(_("Please select advance tax account or add Unrealized Profit / Loss Account in company master")) + + self.advance_tax_account = unrealized_profit_loss_account def validate_journal_entry(self): for d in self.get("references"): @@ -418,8 +422,10 @@ class PaymentEntry(AccountsController): net_total = order_amount[0][0] included_in_paid_amount = 1 + # Adding args as purchase invoice to get TDS amount args = frappe._dict({ 'company': self.company, + 'doctype': 'Purchase Invoice', 'supplier': self.party, 'posting_date': self.posting_date, 'net_total': net_total @@ -727,7 +733,7 @@ class PaymentEntry(AccountsController): rev_dr_cr + "_in_account_currency": d.base_tax_amount \ if account_currency==self.company_currency \ else d.tax_amount, - "cost_center": d.cost_center + "cost_center": d.cost_center or self.cost_center }, account_currency, item=d)) def add_deductions_gl_entries(self, gl_entries): @@ -830,12 +836,14 @@ class PaymentEntry(AccountsController): else: current_tax_amount *= 1.0 - if i == 0: - tax.total = flt(self.paid_amount + current_tax_amount, self.precision("total", tax)) - else: - tax.total = flt(self.taxes[i-1].total + current_tax_amount, self.precision("total", tax)) + if not tax.included_in_paid_amount: + if i == 0: + tax.total = flt(self.paid_amount + current_tax_amount, self.precision("total", tax)) + else: + tax.total = flt(self.taxes[i-1].total + current_tax_amount, self.precision("total", tax)) + + tax.base_total = tax.total * self.source_exchange_rate - tax.base_total = tax.total * self.source_exchange_rate self.total_taxes_and_charges += current_tax_amount self.base_total_taxes_and_charges += current_tax_amount * self.source_exchange_rate diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 8f7248f1e6..5d4796c916 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -935,7 +935,7 @@ class PurchaseInvoice(BuyingController): rev_dr_cr + "_in_account_currency": unallocated_amount \ if account_currency==self.company_currency \ else unallocated_amount, - 'cost_center': tax.cost_center + 'cost_center': tax.cost_center or self.cost_center }, account_currency, item=tax)) frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount + unallocated_amount) 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 5c1cbaa4aa..b9ee4a0963 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -49,7 +49,7 @@ def get_party_tax_withholding_details(inv, tax_withholding_category=None): if not parties: parties.append(party) - fiscal_year = get_fiscal_year(inv.posting_date, company=inv.company) + fiscal_year = get_fiscal_year(inv.get('posting_date') or inv.get('transaction_date'), company=inv.company) tax_details = get_tax_withholding_details(tax_withholding_category, fiscal_year[0], inv.company) if not tax_details: @@ -154,7 +154,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, fiscal_year_details, p tax_deducted = get_deducted_tax(taxable_vouchers, fiscal_year, tax_details) tax_amount = 0 - posting_date = inv.posting_date + posting_date = inv.get('posting_date') or inv.get('transaction_date') if party_type == 'Supplier': ldc = get_lower_deduction_certificate(fiscal_year, pan_no) if tax_deducted: @@ -257,7 +257,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_dedu if ((threshold and inv.net_total >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)): if ldc and is_valid_certificate( ldc.valid_from, ldc.valid_upto, - inv.posting_date, tax_deducted, + inv.get('posting_date') or inv.get('transaction_date'), tax_deducted, inv.net_total, ldc.certificate_limit ): tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js index 344539eef6..011c29621a 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js @@ -54,6 +54,32 @@ frappe.query_reports["TDS Payable Monthly"] = { frappe.query_report.refresh(); } }, + { + "fieldname":"purchase_order", + "label": __("Purchase Order"), + "fieldtype": "Link", + "options": "Purchase Order", + "get_query": function() { + return { + "filters": { + "name": ["in", frappe.query_report.invoices] + } + } + }, + on_change: function() { + let supplier = frappe.query_report.get_filter_value('supplier'); + if(!supplier) return; // return if no supplier selected + + // filter order based on selected supplier + let orders = []; + frappe.query_report.order_data.map(d => { + if(d.supplier==supplier) + orders.push(d.name) + }); + frappe.query_report.orders = orders; + frappe.query_report.refresh(); + } + }, { "fieldname":"from_date", "label": __("From Date"), @@ -75,15 +101,24 @@ frappe.query_reports["TDS Payable Monthly"] = { onload: function(report) { // fetch all tds applied invoices frappe.call({ - "method": "erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly.get_tds_invoices", + "method": "erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly.get_tds_invoices_and_orders", callback: function(r) { let invoices = []; - r.message.map(d => { + let orders = []; + + r.message.invoices.map(d => { invoices.push(d.name); }); - report["invoice_data"] = r.message; + r.message.orders.map(d => { + orders.push(d.name); + }); + + report["invoice_data"] = r.message.invoices; report["invoices"] = invoices; + + report["order_data"] = r.message.orders; + report["invoices"] = orders; } }); } diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index a9fb237a04..7ce61f4a28 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -8,14 +8,18 @@ from frappe.utils import getdate def execute(filters=None): filters["invoices"] = frappe.cache().hget("invoices", frappe.session.user) + filters["orders"] = frappe.cache().hget("orders", frappe.session.user) validate_filters(filters) set_filters(filters) + # TDS payment entries + payment_entries = get_payment_entires(filters) + columns = get_columns(filters) - if not filters["invoices"]: + if not (filters.get("invoices") and filters.get('orders')): return columns, [] - res = get_result(filters) + res = get_result(filters, payment_entries) return columns, res @@ -26,9 +30,14 @@ def validate_filters(filters): def set_filters(filters): invoices = [] + orders = [] + + if not filters.get("invoices"): + filters["invoices"] = get_tds_invoices_and_orders()['invoices'] + + if not filters.get("orders"): + filters["orders"] = get_tds_invoices_and_orders()['orders'] - if not filters["invoices"]: - filters["invoices"] = get_tds_invoices() if filters.supplier and filters.purchase_invoice: for d in filters["invoices"]: if d.name == filters.purchase_invoice and d.supplier == filters.supplier: @@ -42,12 +51,28 @@ def set_filters(filters): if d.name == filters.purchase_invoice: invoices.append(d) + if filters.supplier and filters.purchase_order: + for d in filters.get("orders"): + if d.name == filters.purchase_order and d.supplier == filters.supplier: + orders.append(d) + elif filters.supplier and not filters.purchase_order: + for d in filters.get("orders"): + if d.supplier == filters.supplier: + orders.append(d) + elif filters.purchase_order and not filters.supplier: + for d in filters.get("invoices"): + if d.name == filters.purchase_order: + orders.append(d) + filters["invoices"] = invoices if invoices else filters["invoices"] + filters["orders"] = orders if orders else filters["orders"] filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name') -def get_result(filters): - supplier_map, tds_docs = get_supplier_map(filters) - gle_map = get_gle_map(filters) +def get_result(filters, payment_entries): + supplier_map, tds_docs = get_supplier_map(filters, payment_entries) + documents = [d.get('name') for d in filters.get('invoices')] + [d.get('name') for d in payment_entries] + + gle_map = get_gle_map(filters, documents) out = [] for d in gle_map: @@ -62,10 +87,11 @@ def get_result(filters): for k in gle_map[d]: if k.party == supplier_map[d] and k.credit > 0: - total_amount_credited += k.credit - elif account_list and k.account == account and k.credit > 0: - tds_deducted = k.credit - total_amount_credited += k.credit + total_amount_credited += (k.credit - k.debit) + elif account_list and k.account == account and (k.credit - k.debit) > 0: + tds_deducted = (k.credit - k.debit) + total_amount_credited += (k.credit - k.debit) + voucher_type = k.voucher_type rate = [i.tax_withholding_rate for i in tds_doc.rates if i.fiscal_year == gle_map[d][0].fiscal_year] @@ -73,32 +99,40 @@ def get_result(filters): if rate and len(rate) > 0 and tds_deducted: rate = rate[0] - if getdate(filters.from_date) <= gle_map[d][0].posting_date \ - and getdate(filters.to_date) >= gle_map[d][0].posting_date: - row = [supplier.pan, supplier.name] + row = [supplier.pan, supplier.name] - if filters.naming_series == 'Naming Series': - row.append(supplier.supplier_name) + if filters.naming_series == 'Naming Series': + row.append(supplier.supplier_name) - row.extend([tds_doc.name, supplier.supplier_type, rate, total_amount_credited, - tds_deducted, gle_map[d][0].posting_date, "Purchase Invoice", d]) - out.append(row) + row.extend([tds_doc.name, supplier.supplier_type, rate, total_amount_credited, + tds_deducted, gle_map[d][0].posting_date, voucher_type, d]) + out.append(row) return out -def get_supplier_map(filters): +def get_supplier_map(filters, payment_entries): # create a supplier_map of the form {"purchase_invoice": {supplier_name, pan, tds_name}} # pre-fetch all distinct applicable tds docs supplier_map, tds_docs = {}, {} pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id" + supplier_list = [d.supplier for d in filters["invoices"]] + [d.supplier for d in filters["orders"]] + supplier_detail = frappe.db.get_all('Supplier', - {"name": ["in", [d.supplier for d in filters["invoices"]]]}, + {"name": ["in", supplier_list]}, ["tax_withholding_category", "name", pan+" as pan", "supplier_type", "supplier_name"]) for d in filters["invoices"]: supplier_map[d.get("name")] = [k for k in supplier_detail if k.name == d.get("supplier")][0] + for d in filters["orders"]: + supplier_map[d.get("name")] = [k for k in supplier_detail + if k.name == d.get("supplier")][0] + + for d in payment_entries: + supplier_map[d.get("name")] = [k for k in supplier_detail + if k.name == d.get("supplier")][0] + for d in supplier_detail: if d.get("tax_withholding_category") not in tds_docs: tds_docs[d.get("tax_withholding_category")] = \ @@ -106,13 +140,19 @@ def get_supplier_map(filters): return supplier_map, tds_docs -def get_gle_map(filters): +def get_gle_map(filters, documents): # create gle_map of the form # {"purchase_invoice": list of dict of all gle created for this invoice} gle_map = {} - gle = frappe.db.get_all('GL Entry',\ - {"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]], 'is_cancelled': 0}, - ["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date"]) + filter_obj = {} + gle = frappe.db.get_all('GL Entry', + { + "voucher_no": ["in", documents], + 'is_cancelled': 0, + 'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]), + }, + ["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date", "voucher_type"], + ) for d in gle: if not d.voucher_no in gle_map: @@ -201,8 +241,28 @@ def get_columns(filters): return columns +def get_payment_entires(filters): + filter_dict = { + 'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]), + 'party_type': 'Supplier', + 'apply_tax_withholding_amount': 1 + } + + if filters.get('purchase_order') or filters.get('purchase_invoice'): + parent = frappe.db.get_all('Payment Entry Reference', + { + 'reference_name': ('in', [d.get('name') for d in filters.get('orders')] + + [d.get('name') for d in filters.get('invoices')]) + }, ['parent']) + filter_dict.update({'name': ('in', [d.get('parent') for d in parent])}) + + payment_entries = frappe.get_all('Payment Entry', fields=['name', 'party_name as supplier'], + filters=filter_dict) + + return payment_entries + @frappe.whitelist() -def get_tds_invoices(): +def get_tds_invoices_and_orders(): # fetch tds applicable supplier and fetch invoices for these suppliers suppliers = [d.name for d in frappe.db.get_list("Supplier", {"tax_withholding_category": ["!=", ""]}, ["name"])] @@ -210,7 +270,16 @@ def get_tds_invoices(): invoices = frappe.db.get_list("Purchase Invoice", {"supplier": ["in", suppliers]}, ["name", "supplier"]) - invoices = [d for d in invoices if d.supplier] - frappe.cache().hset("invoices", frappe.session.user, invoices) + orders = frappe.db.get_list("Purchase Order", + {"supplier": ["in", suppliers]}, ["name", "supplier"]) - return invoices + invoices = [d for d in invoices if d.supplier] + orders = [d for d in orders if d.supplier] + + frappe.cache().hset("invoices", frappe.session.user, invoices) + frappe.cache().hset("orders", frappe.session.user, invoices) + + return { + 'invoices': invoices, + 'orders': orders + } diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 64eda5f280..47ec212b12 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1375,7 +1375,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-11-28 17:42:25.177827", + "modified": "2021-02-27 22:07:23.487138", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 9957aad019..9dc7fc42ef 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -276,7 +276,7 @@ erpnext.company.setup_queries = function(frm) { ["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"}], - ["unrealized_profit_loss_account", {"root_type": "Liability"},] + ["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}] ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); From a87e3fcb7c5ddd476fa492b5c22f18f2ba79556e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 28 Feb 2021 23:28:25 +0530 Subject: [PATCH 045/180] fix: TDS report cleanup --- .../doctype/payment_entry/payment_entry.py | 5 +- .../purchase_invoice/purchase_invoice.json | 21 +++++--- .../tds_payable_monthly.js | 19 +++----- .../tds_payable_monthly.py | 48 +++++++------------ 4 files changed, 42 insertions(+), 51 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 33eb6f2f26..c0feb44a7f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -967,7 +967,7 @@ def split_invoices_based_on_payment_terms(outstanding_invoices): outstanding_invoices.pop(idx - 1) outstanding_invoices += invoice_ref_based_on_payment_terms[idx] - + return outstanding_invoices def get_orders_to_be_billed(posting_date, party_type, party, @@ -1411,6 +1411,9 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= }) pe.set_difference_amount() + if doc.doctype == 'Purchase Order' and doc.apply_tds: + pe.apply_tax_withholding_amount = 1 + return pe def get_bank_cash_account(doc, bank_account): diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 36450a9255..7fef60cde1 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -127,7 +127,6 @@ "write_off_cost_center", "advances_section", "allocate_advances_automatically", - "adjust_advance_taxes", "get_advances", "advances", "payment_schedule_section", @@ -1492,7 +1491,9 @@ "in_standard_filter": 1, "label": "Status", "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled\nInternal Transfer", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_invoice_reference", @@ -1501,7 +1502,9 @@ "no_copy": 1, "options": "Sales Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "remarks", @@ -1633,7 +1636,9 @@ "fieldname": "unrealized_profit_loss_account", "fieldtype": "Link", "label": "Unrealized Profit / Loss Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_internal_supplier", @@ -1642,7 +1647,9 @@ "fieldname": "represents_company", "fieldtype": "Link", "label": "Represents Company", - "options": "Company" + "options": "Company", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.update_stock && doc.is_internal_supplier", @@ -1654,6 +1661,8 @@ "options": "Warehouse", "print_hide": 1, "print_width": "50px", + "show_days": 1, + "show_seconds": 1, "width": "50px" }, { @@ -1681,7 +1690,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2020-10-27 21:21:22.647349", + "modified": "2021-02-28 22:33:15.728392", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js index 011c29621a..72de318a48 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js @@ -70,13 +70,13 @@ frappe.query_reports["TDS Payable Monthly"] = { let supplier = frappe.query_report.get_filter_value('supplier'); if(!supplier) return; // return if no supplier selected - // filter order based on selected supplier - let orders = []; - frappe.query_report.order_data.map(d => { + // filter invoices based on selected supplier + let invoices = []; + frappe.query_report.invoice_data.map(d => { if(d.supplier==supplier) - orders.push(d.name) + invoices.push(d.name) }); - frappe.query_report.orders = orders; + frappe.query_report.invoices = invoices; frappe.query_report.refresh(); } }, @@ -104,21 +104,14 @@ frappe.query_reports["TDS Payable Monthly"] = { "method": "erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly.get_tds_invoices_and_orders", callback: function(r) { let invoices = []; - let orders = []; - r.message.invoices.map(d => { + r.message.map(d => { invoices.push(d.name); }); - r.message.orders.map(d => { - orders.push(d.name); - }); - report["invoice_data"] = r.message.invoices; report["invoices"] = invoices; - report["order_data"] = r.message.orders; - report["invoices"] = orders; } }); } diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index 7ce61f4a28..5101e3d752 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -8,7 +8,6 @@ from frappe.utils import getdate def execute(filters=None): filters["invoices"] = frappe.cache().hget("invoices", frappe.session.user) - filters["orders"] = frappe.cache().hget("orders", frappe.session.user) validate_filters(filters) set_filters(filters) @@ -16,7 +15,7 @@ def execute(filters=None): payment_entries = get_payment_entires(filters) columns = get_columns(filters) - if not (filters.get("invoices") and filters.get('orders')): + if not filters.get("invoices"): return columns, [] res = get_result(filters, payment_entries) @@ -30,13 +29,9 @@ def validate_filters(filters): def set_filters(filters): invoices = [] - orders = [] if not filters.get("invoices"): - filters["invoices"] = get_tds_invoices_and_orders()['invoices'] - - if not filters.get("orders"): - filters["orders"] = get_tds_invoices_and_orders()['orders'] + filters["invoices"] = get_tds_invoices_and_orders() if filters.supplier and filters.purchase_invoice: for d in filters["invoices"]: @@ -50,24 +45,25 @@ def set_filters(filters): for d in filters["invoices"]: if d.name == filters.purchase_invoice: invoices.append(d) - - if filters.supplier and filters.purchase_order: - for d in filters.get("orders"): + elif filters.supplier and filters.purchase_order: + for d in filters.get("invoices"): if d.name == filters.purchase_order and d.supplier == filters.supplier: - orders.append(d) + invoices.append(d) elif filters.supplier and not filters.purchase_order: - for d in filters.get("orders"): + for d in filters.get("invoices"): if d.supplier == filters.supplier: - orders.append(d) + invoices.append(d) elif filters.purchase_order and not filters.supplier: for d in filters.get("invoices"): if d.name == filters.purchase_order: - orders.append(d) + print("$#$#$$#") + invoices.append(d) filters["invoices"] = invoices if invoices else filters["invoices"] - filters["orders"] = orders if orders else filters["orders"] filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name') + #print(filters.get('invoices')) + def get_result(filters, payment_entries): supplier_map, tds_docs = get_supplier_map(filters, payment_entries) documents = [d.get('name') for d in filters.get('invoices')] + [d.get('name') for d in payment_entries] @@ -115,7 +111,7 @@ def get_supplier_map(filters, payment_entries): # pre-fetch all distinct applicable tds docs supplier_map, tds_docs = {}, {} pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id" - supplier_list = [d.supplier for d in filters["invoices"]] + [d.supplier for d in filters["orders"]] + supplier_list = [d.supplier for d in filters["invoices"]] supplier_detail = frappe.db.get_all('Supplier', {"name": ["in", supplier_list]}, @@ -125,10 +121,6 @@ def get_supplier_map(filters, payment_entries): supplier_map[d.get("name")] = [k for k in supplier_detail if k.name == d.get("supplier")][0] - for d in filters["orders"]: - supplier_map[d.get("name")] = [k for k in supplier_detail - if k.name == d.get("supplier")][0] - for d in payment_entries: supplier_map[d.get("name")] = [k for k in supplier_detail if k.name == d.get("supplier")][0] @@ -248,12 +240,10 @@ def get_payment_entires(filters): 'apply_tax_withholding_amount': 1 } - if filters.get('purchase_order') or filters.get('purchase_invoice'): + if filters.get('purchase_invoice') or filters.get('purchase_order'): parent = frappe.db.get_all('Payment Entry Reference', - { - 'reference_name': ('in', [d.get('name') for d in filters.get('orders')] + - [d.get('name') for d in filters.get('invoices')]) - }, ['parent']) + { 'reference_name': ('in', [d.get('name') for d in filters.get('invoices')])}, ['parent']) + filter_dict.update({'name': ('in', [d.get('parent') for d in parent])}) payment_entries = frappe.get_all('Payment Entry', fields=['name', 'party_name as supplier'], @@ -273,13 +263,9 @@ def get_tds_invoices_and_orders(): orders = frappe.db.get_list("Purchase Order", {"supplier": ["in", suppliers]}, ["name", "supplier"]) + invoices = invoices + orders invoices = [d for d in invoices if d.supplier] - orders = [d for d in orders if d.supplier] frappe.cache().hset("invoices", frappe.session.user, invoices) - frappe.cache().hset("orders", frappe.session.user, invoices) - return { - 'invoices': invoices, - 'orders': orders - } + return invoices From fd380d34f99b70616cfa06fb6ce30cdccce931fe Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 13 May 2021 14:04:51 +0530 Subject: [PATCH 046/180] fix: Linting errors --- .../doctype/payment_entry/payment_entry.py | 17 +++--- .../purchase_invoice/purchase_invoice.py | 8 +-- .../doctype/sales_invoice/sales_invoice.py | 8 +-- .../tds_payable_monthly.py | 4 +- erpnext/public/js/controllers/accounts.js | 12 ++-- erpnext/public/js/payment/payments.js | 56 +++++++++---------- 6 files changed, 52 insertions(+), 53 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index c0feb44a7f..736048fe3e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -516,13 +516,13 @@ class PaymentEntry(AccountsController): if self.payment_type == "Receive" \ and self.base_total_allocated_amount < self.base_received_amount_after_tax + total_deductions \ and self.total_allocated_amount < self.paid_amount_after_tax + (total_deductions / self.source_exchange_rate): - self.unallocated_amount = (self.received_amount_after_tax + total_deductions - - self.base_total_allocated_amount) / self.source_exchange_rate + self.unallocated_amount = (self.received_amount_after_tax + total_deductions - + self.base_total_allocated_amount) / self.source_exchange_rate elif self.payment_type == "Pay" \ and self.base_total_allocated_amount < (self.base_paid_amount_after_tax - total_deductions) \ and self.total_allocated_amount < self.received_amount_after_tax + (total_deductions / self.target_exchange_rate): - self.unallocated_amount = (self.base_paid_amount_after_tax - (total_deductions + - self.base_total_allocated_amount)) / self.target_exchange_rate + self.unallocated_amount = (self.base_paid_amount_after_tax - (total_deductions + + self.base_total_allocated_amount)) / self.target_exchange_rate def set_difference_amount(self): base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate) @@ -719,8 +719,8 @@ class PaymentEntry(AccountsController): "account": d.account_head, "against": self.party if self.payment_type=="Receive" else self.paid_from, dr_or_cr: d.base_tax_amount, - dr_or_cr + "_in_account_currency": d.base_tax_amount \ - if account_currency==self.company_currency \ + dr_or_cr + "_in_account_currency": d.base_tax_amount + if account_currency==self.company_currency else d.tax_amount, "cost_center": d.cost_center }, account_currency, item=d)) @@ -730,8 +730,8 @@ class PaymentEntry(AccountsController): "account": self.advance_tax_account, "against": self.party if self.payment_type=="Receive" else self.paid_from, rev_dr_cr: d.base_tax_amount, - rev_dr_cr + "_in_account_currency": d.base_tax_amount \ - if account_currency==self.company_currency \ + rev_dr_cr + "_in_account_currency": d.base_tax_amount + if account_currency==self.company_currency else d.tax_amount, "cost_center": d.cost_center or self.cost_center }, account_currency, item=d)) @@ -806,7 +806,6 @@ class PaymentEntry(AccountsController): for i, tax in enumerate(self.taxes): tax_rate = tax.rate - current_tax_rate = 0.0 # To set row_id by default as previous row. if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"]: diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 5d4796c916..83c55cc653 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -921,8 +921,8 @@ class PurchaseInvoice(BuyingController): "account": tax.account_head, "against": self.supplier, dr_or_cr: unallocated_amount, - dr_or_cr + "_in_account_currency": unallocated_amount \ - if account_currency==self.company_currency \ + dr_or_cr + "_in_account_currency": unallocated_amount + if account_currency==self.company_currency else unallocated_amount, 'cost_center': tax.cost_center }, account_currency, item=tax)) @@ -932,8 +932,8 @@ class PurchaseInvoice(BuyingController): "account": pe.advance_tax_account, "against": self.supplier, rev_dr_cr: unallocated_amount, - rev_dr_cr + "_in_account_currency": unallocated_amount \ - if account_currency==self.company_currency \ + rev_dr_cr + "_in_account_currency": unallocated_amount + if account_currency==self.company_currency else unallocated_amount, 'cost_center': tax.cost_center or self.cost_center }, account_currency, item=tax)) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 9af94fd780..44b8325c62 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -933,8 +933,8 @@ class SalesInvoice(SellingController): "account": tax.account_head, "against": self.customer, dr_or_cr: unallocated_amount, - dr_or_cr + "_in_account_currency": unallocated_amount \ - if account_currency==self.company_currency \ + dr_or_cr + "_in_account_currency": unallocated_amount + if account_currency==self.company_currency else unallocated_amount, 'cost_center': tax.cost_center }, account_currency, item=tax)) @@ -944,8 +944,8 @@ class SalesInvoice(SellingController): "account": pe.advance_tax_account, "against": self.customer, rev_dr_cr: unallocated_amount, - rev_dr_cr + "_in_account_currency": unallocated_amount \ - if account_currency==self.company_currency \ + rev_dr_cr + "_in_account_currency": unallocated_amount + if account_currency==self.company_currency else unallocated_amount, 'cost_center': tax.cost_center }, account_currency, item=tax)) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index 5101e3d752..619d799d06 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -136,7 +136,7 @@ def get_gle_map(filters, documents): # create gle_map of the form # {"purchase_invoice": list of dict of all gle created for this invoice} gle_map = {} - filter_obj = {} + gle = frappe.db.get_all('GL Entry', { "voucher_no": ["in", documents], @@ -242,7 +242,7 @@ def get_payment_entires(filters): if filters.get('purchase_invoice') or filters.get('purchase_order'): parent = frappe.db.get_all('Payment Entry Reference', - { 'reference_name': ('in', [d.get('name') for d in filters.get('invoices')])}, ['parent']) + {'reference_name': ('in', [d.get('name') for d in filters.get('invoices')])}, ['parent']) filter_dict.update({'name': ('in', [d.get('parent') for d in parent])}) diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 23d636061c..7b997a1153 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -156,31 +156,31 @@ cur_frm.cscript.validate_taxes_and_charges = function(cdt, cdn) { var d = locals[cdt][cdn]; var msg = ""; - if(d.account_head && !d.description) { + if (d.account_head && !d.description) { // set description from account head d.description = d.account_head.split(' - ').slice(0, -1).join(' - '); } - if(!d.charge_type && (d.row_id || d.rate || d.tax_amount)) { + if (!d.charge_type && (d.row_id || d.rate || d.tax_amount)) { msg = __("Please select Charge Type first"); d.row_id = ""; d.rate = d.tax_amount = 0.0; - } else if((d.charge_type == 'Actual' || d.charge_type == 'On Net Total' || d.charge_type == 'On Paid Amount') && d.row_id) { + } else if ((d.charge_type == 'Actual' || d.charge_type == 'On Net Total' || d.charge_type == 'On Paid Amount') && d.row_id) { msg = __("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'"); d.row_id = ""; - } else if((d.charge_type == 'On Previous Row Amount' || d.charge_type == 'On Previous Row Total') && d.row_id) { + } else if ((d.charge_type == 'On Previous Row Amount' || d.charge_type == 'On Previous Row Total') && d.row_id) { if (d.idx == 1) { msg = __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"); d.charge_type = ''; } else if (!d.row_id) { msg = __("Please specify a valid Row ID for row {0} in table {1}", [d.idx, __(d.doctype)]); d.row_id = ""; - } else if(d.row_id && d.row_id >= d.idx) { + } else if (d.row_id && d.row_id >= d.idx) { msg = __("Cannot refer row number greater than or equal to current row number for this Charge type"); d.row_id = ""; } } - if(msg) { + if (msg) { frappe.validated = false; refresh_field("taxes"); frappe.throw(msg); diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js index b168952e40..e2bd602ba7 100644 --- a/erpnext/public/js/payment/payments.js +++ b/erpnext/public/js/payment/payments.js @@ -16,14 +16,14 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.select_text() }, - select_text: function(){ + select_text: function() { var me = this; - $(this.$body).find('.form-control').click(function(){ + $(this.$body).find('.form-control').click(function() { $(this).select(); }) }, - set_payment_primary_action: function(){ + set_payment_primary_action: function() { var me = this; this.dialog.set_primary_action(__("Submit"), function() { @@ -38,7 +38,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ }) }, - make_keyboard: function(){ + make_keyboard: function() { var me = this; $(this.$body).empty(); $(this.$body).html(frappe.render_template('pos_payment', this.frm.doc)) @@ -47,10 +47,10 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.clear_amount() }, - make_multimode_payment: function(){ + make_multimode_payment: function() { var me = this; - if(this.frm.doc.change_amount > 0){ + if(this.frm.doc.change_amount > 0) { me.payment_val = me.doc.outstanding_amount } @@ -59,7 +59,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.payments.amount = flt(this.payment_val); }, - show_payment_details: function(){ + show_payment_details: function() { var me = this; var multimode_payments = $(this.$body).find('.multimode-payments').empty(); if(this.frm.doc.payments.length){ @@ -84,7 +84,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ } }, - set_outstanding_amount: function(){ + 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 @@ -101,45 +101,45 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.bind_amount_change_event(); }, - bind_keyboard_event: function(){ + 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(){ + bind_form_control_event: function() { var me = this; - $(this.$body).find('.pos-payment-row').click(function(){ + $(this.$body).find('.pos-payment-row').click(function() { me.idx = $(this).attr("idx"); me.set_outstanding_amount() - }) + }); - $(this.$body).find('.form-control').click(function(){ + $(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(){ + $(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(){ + $(this.$body).find('.change_amount').change(function() { me.change_amount(flt($(this).val()), precision("change_amount")); - }) + }); }, - highlight_selected_row: function(){ + highlight_selected_row: function() { var me = this; - var selected_row = $(this.$body).find(repl(".pos-payment-row[idx='%(idx)s']",{'idx': this.idx})); + 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(){ + bind_numeric_keys_event: function() { var me = this; $(this.$body).find('.pos-keyboard-key').click(function(){ me.payment_val += $(this).text(); @@ -148,7 +148,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ me.update_paid_amount() }) - $(this.$body).find('.delete-btn').click(function(){ + $(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") @@ -157,9 +157,9 @@ erpnext.payments = erpnext.stock.StockController.extend({ }, - bind_amount_change_event: function(){ + bind_amount_change_event: function() { var me = this; - this.selected_mode.change(function(){ + 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") @@ -169,7 +169,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ clear_amount: function() { var me = this; - $(this.$body).find('.clr').click(function(e){ + $(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})); @@ -215,10 +215,10 @@ erpnext.payments = erpnext.stock.StockController.extend({ } }, - update_payment_amount: function(){ + update_payment_amount: function() { var me = this; - $.each(this.frm.doc.payments, function(index, data){ + $.each(this.frm.doc.payments, function(index, data) { if(cint(me.idx) == cint(data.idx)){ data.amount = flt(me.selected_mode.val(), 2) } @@ -228,7 +228,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.show_amounts(); }, - show_amounts: function(){ + 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)); From 77dcee9d677c3f1833c213c50a9369c373991589 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 16 May 2021 17:45:45 +0530 Subject: [PATCH 047/180] fix: Test Cases --- .../doctype/payment_entry/payment_entry.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 736048fe3e..ac8e7070f5 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -482,7 +482,7 @@ class PaymentEntry(AccountsController): self.received_amount_after_tax = flt(flt(self.received_amount) + flt(applicable_tax), self.precision("paid_amount_after_tax")) - self.base_received_amount_after_tax = flt(flt(self.received_amount_after_tax) * flt(self.source_exchange_rate), + self.base_received_amount_after_tax = flt(flt(self.received_amount_after_tax) * flt(self.target_exchange_rate), self.precision("base_paid_amount_after_tax")) def set_amounts_in_company_currency(self): @@ -720,8 +720,8 @@ class PaymentEntry(AccountsController): "against": self.party if self.payment_type=="Receive" else self.paid_from, dr_or_cr: d.base_tax_amount, dr_or_cr + "_in_account_currency": d.base_tax_amount - if account_currency==self.company_currency - else d.tax_amount, + if account_currency==self.company_currency + else d.tax_amount, "cost_center": d.cost_center }, account_currency, item=d)) @@ -731,8 +731,8 @@ class PaymentEntry(AccountsController): "against": self.party if self.payment_type=="Receive" else self.paid_from, rev_dr_cr: d.base_tax_amount, rev_dr_cr + "_in_account_currency": d.base_tax_amount - if account_currency==self.company_currency - else d.tax_amount, + if account_currency==self.company_currency + else d.tax_amount, "cost_center": d.cost_center or self.cost_center }, account_currency, item=d)) @@ -804,7 +804,7 @@ class PaymentEntry(AccountsController): self.total_taxes_and_charges = 0.0 self.base_total_taxes_and_charges = 0.0 - for i, tax in enumerate(self.taxes): + for i, tax in enumerate(self.get('taxes')): tax_rate = tax.rate # To set row_id by default as previous row. @@ -821,11 +821,11 @@ class PaymentEntry(AccountsController): current_tax_amount = (tax_rate / 100.0) * self.paid_amount elif tax.charge_type == "On Previous Row Amount": current_tax_amount = (tax_rate / 100.0) * \ - self.taxes[cint(tax.row_id) - 1].tax_amount + self.get('taxes')[cint(tax.row_id) - 1].tax_amount elif tax.charge_type == "On Previous Row Total": current_tax_amount = (tax_rate / 100.0) * \ - self.taxes[cint(tax.row_id) - 1].total + self.get('taxes')[cint(tax.row_id) - 1].total tax.tax_amount = current_tax_amount tax.base_tax_amount = tax.tax_amount * self.source_exchange_rate @@ -839,7 +839,7 @@ class PaymentEntry(AccountsController): if i == 0: tax.total = flt(self.paid_amount + current_tax_amount, self.precision("total", tax)) else: - tax.total = flt(self.taxes[i-1].total + current_tax_amount, self.precision("total", tax)) + tax.total = flt(self.get('taxes')[i-1].total + current_tax_amount, self.precision("total", tax)) tax.base_total = tax.total * self.source_exchange_rate From e2f83ffaa4c5770210fedb8ec22d433fb21378b3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 16 May 2021 17:46:07 +0530 Subject: [PATCH 048/180] fix: Linting fixes and other checks --- .../doctype/payment_entry/payment_entry.json | 2 +- .../purchase_invoice/purchase_invoice.py | 31 +++++----- .../doctype/sales_invoice/sales_invoice.py | 8 +-- .../doctype/purchase_order/purchase_order.js | 4 +- erpnext/public/js/payment/payments.js | 62 +++++++++---------- 5 files changed, 53 insertions(+), 54 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index d239c41fd8..afc72045e6 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -864,7 +864,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-02-27 13:56:20.007336", + "modified": "2021-05-15 13:05:16.958866", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 83c55cc653..a4bac3550d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -251,11 +251,9 @@ class PurchaseInvoice(BuyingController): if self.update_stock and (not item.from_warehouse): if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]: - msg = _("Row {}: Expense Head changed to {} ").format(item.idx, frappe.bold(warehouse_account[item.warehouse]["account"])) - msg += _("because account {} is not linked to warehouse {} ").format(frappe.bold(item.expense_account), frappe.bold(item.warehouse)) - msg += _("or it is not the default inventory account") + msg = _("Row {0}: Expense Head changed to {1} because account {2} is not linked to warehouse {3} or it is not the default inventory account").format( + item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]), frappe.bold(item.expense_account), frappe.bold(item.warehouse)) frappe.msgprint(msg, title=_("Expense Head Changed")) - item.expense_account = warehouse_account[item.warehouse]["account"] else: # check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not @@ -266,8 +264,8 @@ class PurchaseInvoice(BuyingController): if negative_expense_booked_in_pr: if for_validate and item.expense_account and item.expense_account != stock_not_billed_account: - msg = _("Row {}: Expense Head changed to {} ").format(item.idx, frappe.bold(stock_not_billed_account)) - msg += _("because expense is booked against this account in Purchase Receipt {}").format(frappe.bold(item.purchase_receipt)) + msg = _("Row {0}: Expense Head changed to {1} because expense is booked against this account in Purchase Receipt {2}").format( + item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt)) frappe.msgprint(msg, title=_("Expense Head Changed")) item.expense_account = stock_not_billed_account @@ -275,8 +273,9 @@ class PurchaseInvoice(BuyingController): # If no purchase receipt present then book expense in 'Stock Received But Not Billed' # This is done in cases when Purchase Invoice is created before Purchase Receipt if for_validate and item.expense_account and item.expense_account != stock_not_billed_account: - msg = _("Row {}: Expense Head changed to {} ").format(item.idx, frappe.bold(stock_not_billed_account)) - msg += _("as no Purchase Receipt is created against Item {}. ").format(frappe.bold(item.item_code)) + msg = _("Row {0}: Expense Head changed to {1} as no Purchase Receipt is created against Item {2}.").format( + item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.item_code)) + msg += "
" msg += _("This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice") frappe.msgprint(msg, title=_("Expense Head Changed")) @@ -308,8 +307,8 @@ class PurchaseInvoice(BuyingController): if not d.purchase_order: msg = _("Purchase Order Required for item {}").format(frappe.bold(d.item_code)) msg += "

" - msg += _("To submit the invoice without purchase order please set {} ").format(frappe.bold(_('Purchase Order Required'))) - msg += _("as {} in {}").format(frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings')) + msg += _("To submit the invoice without purchase order please set {0} as {1} in {2}").format( + frappe.bold(_('Purchase Order Required')), frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings')) throw(msg, title=_("Mandatory Purchase Order")) def pr_required(self): @@ -323,8 +322,8 @@ class PurchaseInvoice(BuyingController): if not d.purchase_receipt and d.item_code in stock_items: msg = _("Purchase Receipt Required for item {}").format(frappe.bold(d.item_code)) msg += "

" - msg += _("To submit the invoice without purchase receipt please set {} ").format(frappe.bold(_('Purchase Receipt Required'))) - msg += _("as {} in {}").format(frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings')) + msg += _("To submit the invoice without purchase receipt please set {0} as {1} in {2}").format( + frappe.bold(_('Purchase Receipt Required')), frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings')) throw(msg, title=_("Mandatory Purchase Receipt")) def validate_write_off_account(self): @@ -922,8 +921,8 @@ class PurchaseInvoice(BuyingController): "against": self.supplier, dr_or_cr: unallocated_amount, dr_or_cr + "_in_account_currency": unallocated_amount - if account_currency==self.company_currency - else unallocated_amount, + if account_currency==self.company_currency + else unallocated_amount, 'cost_center': tax.cost_center }, account_currency, item=tax)) @@ -933,8 +932,8 @@ class PurchaseInvoice(BuyingController): "against": self.supplier, rev_dr_cr: unallocated_amount, rev_dr_cr + "_in_account_currency": unallocated_amount - if account_currency==self.company_currency - else unallocated_amount, + if account_currency==self.company_currency + else unallocated_amount, 'cost_center': tax.cost_center or self.cost_center }, account_currency, item=tax)) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 44b8325c62..9a596f2b32 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -934,8 +934,8 @@ class SalesInvoice(SellingController): "against": self.customer, dr_or_cr: unallocated_amount, dr_or_cr + "_in_account_currency": unallocated_amount - if account_currency==self.company_currency - else unallocated_amount, + if account_currency==self.company_currency + else unallocated_amount, 'cost_center': tax.cost_center }, account_currency, item=tax)) @@ -945,8 +945,8 @@ class SalesInvoice(SellingController): "against": self.customer, rev_dr_cr: unallocated_amount, rev_dr_cr + "_in_account_currency": unallocated_amount - if account_currency==self.company_currency - else unallocated_amount, + if account_currency==self.company_currency + else unallocated_amount, 'cost_center': tax.cost_center }, account_currency, item=tax)) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 139aa72c08..ccb3ce33ec 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -321,7 +321,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( if(me.values) { me.values.sub_con_rm_items.map((row,i) => { if (!row.item_code || !row.rm_item_code || !row.warehouse || !row.qty || row.qty === 0) { - frappe.throw(__("Item Code, warehouse, quantity are required on row {0}", [i+1])); + frappe.throw(__("Item Code and warehouse and quantity are required on row {0}", [i+1])); } }) me._make_rm_stock_entry(me.dialog.fields_dict.sub_con_rm_items.grid.get_selected_children()) @@ -517,7 +517,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( args: { reference_doctype: me.frm.doctype, reference_name: me.frm.docname, - content: __('Reason for hold: ')+data.reason_for_hold, + content: __('Reason for hold:') + " " +data.reason_for_hold, comment_email: frappe.session.user, comment_by: frappe.session.user_fullname }, diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js index e2bd602ba7..9fb2f3ee7f 100644 --- a/erpnext/public/js/payment/payments.js +++ b/erpnext/public/js/payment/payments.js @@ -13,7 +13,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.$body = this.dialog.body; this.set_payment_primary_action(); this.make_keyboard(); - this.select_text() + this.select_text(); }, select_text: function() { @@ -43,15 +43,15 @@ erpnext.payments = erpnext.stock.StockController.extend({ $(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() + 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 + 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"); @@ -112,7 +112,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ var me = this; $(this.$body).find('.pos-payment-row').click(function() { me.idx = $(this).attr("idx"); - me.set_outstanding_amount() + me.set_outstanding_amount(); }); $(this.$body).find('.form-control').click(function() { @@ -133,8 +133,8 @@ erpnext.payments = erpnext.stock.StockController.extend({ 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('.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); }, @@ -143,15 +143,15 @@ erpnext.payments = erpnext.stock.StockController.extend({ 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() - }) + 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.idx = me.selected_mode.attr("idx"); me.update_paid_amount(); }) @@ -161,10 +161,10 @@ erpnext.payments = erpnext.stock.StockController.extend({ 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() - }) + 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() { @@ -177,7 +177,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ me.selected_mode.val(0.0); me.highlight_selected_row(); me.update_payment_amount(); - }) + }); }, write_off_amount: function(write_off_amount) { @@ -186,32 +186,32 @@ erpnext.payments = erpnext.stock.StockController.extend({ 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() + 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() + 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)){ + 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) + 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) + me.write_off_amount(value); } - }else{ - this.update_payment_amount() + } else { + this.update_payment_amount(); } }, @@ -220,7 +220,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ $.each(this.frm.doc.payments, function(index, data) { if(cint(me.idx) == cint(data.idx)){ - data.amount = flt(me.selected_mode.val(), 2) + data.amount = flt(me.selected_mode.val(), 2); } }) @@ -232,8 +232,8 @@ erpnext.payments = erpnext.stock.StockController.extend({ 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.$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 a23aaf43f47d515a8ec2a848a9341b560aa92706 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 17 May 2021 20:58:50 +0530 Subject: [PATCH 049/180] fix: Allocate advance taxes only for payment entry --- .../purchase_invoice/purchase_invoice.py | 65 ++++++++++--------- .../doctype/sales_invoice/sales_invoice.py | 65 ++++++++++--------- erpnext/controllers/accounts_controller.py | 19 +++--- 3 files changed, 76 insertions(+), 73 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index a4bac3550d..f925bcf5ba 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -903,42 +903,43 @@ class PurchaseInvoice(BuyingController): def allocate_advance_taxes(self, gl_entries): tax_map = self.get_tax_map() for pe in self.get('advances'): - pe = frappe.get_doc('Payment Entry', pe.reference_name) - for tax in pe.get('taxes'): - account_currency = get_account_currency(tax.account_head) - dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" - rev_dr_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" + if pe.reference_type == 'Payment Entry': + pe = frappe.get_doc('Payment Entry', pe.reference_name) + for tax in pe.get('taxes'): + account_currency = get_account_currency(tax.account_head) + dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" + rev_dr_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" - unallocated_amount = tax.tax_amount - tax.allocated_amount - if tax_map.get(tax.account_head): - amount = tax_map.get(tax.account_head) - if amount < unallocated_amount: - unallocated_amount = amount + unallocated_amount = tax.tax_amount - tax.allocated_amount + if tax_map.get(tax.account_head): + amount = tax_map.get(tax.account_head) + if amount < unallocated_amount: + unallocated_amount = amount - gl_entries.append( - self.get_gl_dict({ - "account": tax.account_head, - "against": self.supplier, - dr_or_cr: unallocated_amount, - dr_or_cr + "_in_account_currency": unallocated_amount - if account_currency==self.company_currency - else unallocated_amount, - 'cost_center': tax.cost_center - }, account_currency, item=tax)) + gl_entries.append( + self.get_gl_dict({ + "account": tax.account_head, + "against": self.supplier, + dr_or_cr: unallocated_amount, + dr_or_cr + "_in_account_currency": unallocated_amount + if account_currency==self.company_currency + else unallocated_amount, + 'cost_center': tax.cost_center + }, account_currency, item=tax)) - gl_entries.append( - self.get_gl_dict({ - "account": pe.advance_tax_account, - "against": self.supplier, - rev_dr_cr: unallocated_amount, - rev_dr_cr + "_in_account_currency": unallocated_amount - if account_currency==self.company_currency - else unallocated_amount, - 'cost_center': tax.cost_center or self.cost_center - }, account_currency, item=tax)) + gl_entries.append( + self.get_gl_dict({ + "account": pe.advance_tax_account, + "against": self.supplier, + rev_dr_cr: unallocated_amount, + rev_dr_cr + "_in_account_currency": unallocated_amount + if account_currency==self.company_currency + else unallocated_amount, + 'cost_center': tax.cost_center or self.cost_center + }, account_currency, item=tax)) - frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount + unallocated_amount) - tax_map[tax.account_head] -= unallocated_amount + frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount + unallocated_amount) + tax_map[tax.account_head] -= unallocated_amount def make_payment_gl_entries(self, gl_entries): # Make Cash GL Entries diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 9a596f2b32..664b3ab0f2 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -916,42 +916,43 @@ class SalesInvoice(SellingController): def allocate_advance_taxes(self, gl_entries): tax_map = self.get_tax_map() for pe in self.get('advances'): - pe = frappe.get_doc('Payment Entry', pe.reference_name) - for tax in pe.get('taxes'): - account_currency = get_account_currency(tax.account_head) - dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" - rev_dr_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" + if pe.reference_type == 'Payment Entry': + pe = frappe.get_doc('Payment Entry', pe.reference_name) + for tax in pe.get('taxes'): + account_currency = get_account_currency(tax.account_head) + dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" + rev_dr_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" - unallocated_amount = tax.tax_amount - tax.allocated_amount - if tax_map.get(tax.account_head): - amount = tax_map.get(tax.account_head) - if amount < unallocated_amount: - unallocated_amount = amount + unallocated_amount = tax.tax_amount - tax.allocated_amount + if tax_map.get(tax.account_head): + amount = tax_map.get(tax.account_head) + if amount < unallocated_amount: + unallocated_amount = amount - gl_entries.append( - self.get_gl_dict({ - "account": tax.account_head, - "against": self.customer, - dr_or_cr: unallocated_amount, - dr_or_cr + "_in_account_currency": unallocated_amount - if account_currency==self.company_currency - else unallocated_amount, - 'cost_center': tax.cost_center - }, account_currency, item=tax)) + gl_entries.append( + self.get_gl_dict({ + "account": tax.account_head, + "against": self.customer, + dr_or_cr: unallocated_amount, + dr_or_cr + "_in_account_currency": unallocated_amount + if account_currency==self.company_currency + else unallocated_amount, + 'cost_center': tax.cost_center + }, account_currency, item=tax)) - gl_entries.append( - self.get_gl_dict({ - "account": pe.advance_tax_account, - "against": self.customer, - rev_dr_cr: unallocated_amount, - rev_dr_cr + "_in_account_currency": unallocated_amount - if account_currency==self.company_currency - else unallocated_amount, - 'cost_center': tax.cost_center - }, account_currency, item=tax)) + gl_entries.append( + self.get_gl_dict({ + "account": pe.advance_tax_account, + "against": self.customer, + rev_dr_cr: unallocated_amount, + rev_dr_cr + "_in_account_currency": unallocated_amount + if account_currency==self.company_currency + else unallocated_amount, + 'cost_center': tax.cost_center + }, account_currency, item=tax)) - frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount + unallocated_amount) - tax_map[tax.account_head] -= unallocated_amount + frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount + unallocated_amount) + tax_map[tax.account_head] -= unallocated_amount def make_item_gl_entries(self, gl_entries): # income account gl entries diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index da88853a96..11a9d90a1f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -726,16 +726,17 @@ class AccountsController(TransactionBase): tax_map = self.get_tax_map() for pe in self.get('advances'): - pe = frappe.get_doc('Payment Entry', pe.reference_name) - for tax in pe.get('taxes'): - allocated_amount = tax_map.get(tax.account_head) - allocated_tax_map.get(tax.account_head) - if allocated_amount > tax.tax_amount: - allocated_amount = tax.tax_amount + if pe.reference_type == 'Payment Entry': + pe = frappe.get_doc('Payment Entry', pe.reference_name) + for tax in pe.get('taxes'): + allocated_amount = tax_map.get(tax.account_head) - allocated_tax_map.get(tax.account_head) + if allocated_amount > tax.tax_amount: + allocated_amount = tax.tax_amount - if allocated_amount: - frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount - allocated_amount) - tax_map[tax.account_head] -= allocated_amount - allocated_tax_map[tax.account_head] -= allocated_amount + if allocated_amount: + frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount - allocated_amount) + tax_map[tax.account_head] -= allocated_amount + allocated_tax_map[tax.account_head] -= allocated_amount def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield): from erpnext.controllers.status_updater import get_allowance_for From c9da1fc568db4d6a163b3ba755ec69e776bcea69 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 19 May 2021 18:38:35 +0530 Subject: [PATCH 050/180] chore: Test case for adance TDS allocation --- .../doctype/payment_entry/payment_entry.json | 333 +++++------------- .../doctype/payment_entry/payment_entry.py | 6 +- .../purchase_invoice/purchase_invoice.py | 41 --- .../purchase_invoice/test_purchase_invoice.py | 90 +++++ .../doctype/sales_invoice/sales_invoice.py | 41 --- erpnext/controllers/accounts_controller.py | 56 ++- 6 files changed, 233 insertions(+), 334 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index afc72045e6..b4b92f926e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -97,9 +97,7 @@ { "fieldname": "type_of_payment", "fieldtype": "Section Break", - "label": "Type of Payment", - "show_days": 1, - "show_seconds": 1 + "label": "Type of Payment" }, { "bold": 1, @@ -109,9 +107,7 @@ "options": "ACC-PAY-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1, - "show_days": 1, - "show_seconds": 1 + "set_only_once": 1 }, { "bold": 1, @@ -122,15 +118,11 @@ "label": "Payment Type", "options": "Receive\nPay\nInternal Transfer", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "column_break_5", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "bold": 1, @@ -139,9 +131,7 @@ "fieldtype": "Date", "in_list_view": 1, "label": "Posting Date", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "company", @@ -150,34 +140,26 @@ "options": "Company", "print_hide": 1, "remember_last_selected_value": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "cost_center", "fieldtype": "Link", "label": "Cost Center", - "options": "Cost Center", - "show_days": 1, - "show_seconds": 1 + "options": "Cost Center" }, { "fieldname": "mode_of_payment", "fieldtype": "Link", "in_list_view": 1, "label": "Mode of Payment", - "options": "Mode of Payment", - "show_days": 1, - "show_seconds": 1 + "options": "Mode of Payment" }, { "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type)", "fieldname": "party_section", "fieldtype": "Section Break", - "label": "Payment From / To", - "show_days": 1, - "show_seconds": 1 + "label": "Payment From / To" }, { "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type) && doc.docstatus==0", @@ -187,9 +169,7 @@ "label": "Party Type", "options": "DocType", "print_hide": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "bold": 1, @@ -198,9 +178,7 @@ "fieldtype": "Dynamic Link", "in_standard_filter": 1, "label": "Party", - "options": "party_type", - "show_days": 1, - "show_seconds": 1 + "options": "party_type" }, { "allow_on_submit": 1, @@ -208,24 +186,18 @@ "fieldname": "party_name", "fieldtype": "Data", "in_global_search": 1, - "label": "Party Name", - "show_days": 1, - "show_seconds": 1 + "label": "Party Name" }, { "fieldname": "column_break_11", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "depends_on": "party", "fieldname": "contact_person", "fieldtype": "Link", "label": "Contact", - "options": "Contact", - "show_days": 1, - "show_seconds": 1 + "options": "Contact" }, { "depends_on": "contact_person", @@ -233,17 +205,13 @@ "fieldtype": "Data", "label": "Email", "options": "Email", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "fieldname": "payment_accounts_section", "fieldtype": "Section Break", - "label": "Accounts", - "show_days": 1, - "show_seconds": 1 + "label": "Accounts" }, { "depends_on": "party", @@ -251,9 +219,7 @@ "fieldtype": "Currency", "label": "Party Balance", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "bold": 1, @@ -264,9 +230,7 @@ "label": "Account Paid From", "options": "Account", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "depends_on": "paid_from", @@ -276,9 +240,7 @@ "options": "Currency", "print_hide": 1, "read_only": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "depends_on": "paid_from", @@ -287,15 +249,11 @@ "label": "Account Balance", "options": "paid_from_account_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_18", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "depends_on": "eval:(in_list([\"Internal Transfer\", \"Receive\"], doc.payment_type) || doc.party)", @@ -305,9 +263,7 @@ "label": "Account Paid To", "options": "Account", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "depends_on": "paid_to", @@ -317,9 +273,7 @@ "options": "Currency", "print_hide": 1, "read_only": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "depends_on": "paid_to", @@ -328,17 +282,13 @@ "label": "Account Balance", "options": "paid_to_account_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "eval:(doc.paid_to && doc.paid_from)", "fieldname": "payment_amounts_section", "fieldtype": "Section Break", - "label": "Amount", - "show_days": 1, - "show_seconds": 1 + "label": "Amount" }, { "bold": 1, @@ -346,18 +296,14 @@ "fieldtype": "Currency", "label": "Paid Amount", "options": "paid_from_account_currency", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "source_exchange_rate", "fieldtype": "Float", "label": "Exchange Rate", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "base_paid_amount", @@ -366,15 +312,11 @@ "options": "Company:company:default_currency", "print_hide": 1, "read_only": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "column_break_21", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "bold": 1, @@ -383,18 +325,14 @@ "label": "Received Amount", "options": "paid_to_account_currency", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "target_exchange_rate", "fieldtype": "Float", "label": "Exchange Rate", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "base_received_amount", @@ -403,40 +341,30 @@ "options": "Company:company:default_currency", "print_hide": 1, "read_only": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)", "fieldname": "section_break_14", "fieldtype": "Section Break", - "label": "Reference", - "show_days": 1, - "show_seconds": 1 + "label": "Reference" }, { "depends_on": "eval:doc.docstatus==0", "fieldname": "get_outstanding_invoice", "fieldtype": "Button", - "label": "Get Outstanding Invoice", - "show_days": 1, - "show_seconds": 1 + "label": "Get Outstanding Invoice" }, { "fieldname": "references", "fieldtype": "Table", "label": "Payment References", - "options": "Payment Entry Reference", - "show_days": 1, - "show_seconds": 1 + "options": "Payment Entry Reference" }, { "fieldname": "section_break_34", "fieldtype": "Section Break", - "label": "Writeoff", - "show_days": 1, - "show_seconds": 1 + "label": "Writeoff" }, { "bold": 1, @@ -445,9 +373,7 @@ "fieldtype": "Currency", "label": "Total Allocated Amount", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_total_allocated_amount", @@ -455,31 +381,23 @@ "label": "Total Allocated Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "set_exchange_gain_loss", "fieldtype": "Button", - "label": "Set Exchange Gain / Loss", - "show_days": 1, - "show_seconds": 1 + "label": "Set Exchange Gain / Loss" }, { "fieldname": "column_break_36", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "depends_on": "eval:(doc.paid_amount && doc.received_amount && doc.references)", "fieldname": "unallocated_amount", "fieldtype": "Currency", "label": "Unallocated Amount", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "bold": 1, @@ -489,17 +407,13 @@ "label": "Difference Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "difference_amount", "fieldname": "write_off_difference_amount", "fieldtype": "Button", - "label": "Write Off Difference Amount", - "show_days": 1, - "show_seconds": 1 + "label": "Write Off Difference Amount" }, { "collapsible": 1, @@ -507,39 +421,29 @@ "depends_on": "eval:(doc.paid_amount && doc.received_amount)", "fieldname": "deductions_or_loss_section", "fieldtype": "Section Break", - "label": "Deductions or Loss", - "show_days": 1, - "show_seconds": 1 + "label": "Deductions or Loss" }, { "fieldname": "deductions", "fieldtype": "Table", "label": "Payment Deductions or Loss", - "options": "Payment Entry Deduction", - "show_days": 1, - "show_seconds": 1 + "options": "Payment Entry Deduction" }, { "fieldname": "transaction_references", "fieldtype": "Section Break", - "label": "Transaction ID", - "show_days": 1, - "show_seconds": 1 + "label": "Transaction ID" }, { "bold": 1, "depends_on": "eval:(doc.paid_from && doc.paid_to)", "fieldname": "reference_no", "fieldtype": "Data", - "label": "Cheque/Reference No", - "show_days": 1, - "show_seconds": 1 + "label": "Cheque/Reference No" }, { "fieldname": "column_break_23", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "bold": 1, @@ -547,9 +451,7 @@ "fieldname": "reference_date", "fieldtype": "Date", "label": "Cheque/Reference Date", - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "depends_on": "eval:doc.docstatus==1", @@ -558,76 +460,58 @@ "label": "Clearance Date", "no_copy": 1, "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "depends_on": "eval:(doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)", "fieldname": "section_break_12", "fieldtype": "Section Break", - "label": "More Information", - "show_days": 1, - "show_seconds": 1 + "label": "More Information" }, { "fieldname": "project", "fieldtype": "Link", "label": "Project", "options": "Project", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "remarks", "fieldtype": "Small Text", "label": "Remarks", "no_copy": 1, - "read_only_depends_on": "eval:doc.custom_remarks == 0", - "show_days": 1, - "show_seconds": 1 + "read_only_depends_on": "eval:doc.custom_remarks == 0" }, { "fieldname": "column_break_16", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "letter_head", "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "print_heading", "fieldtype": "Link", "label": "Print Heading", "options": "Print Heading", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fetch_from": "bank_account.bank", "fieldname": "bank", "fieldtype": "Read Only", - "label": "Bank", - "show_days": 1, - "show_seconds": 1 + "label": "Bank" }, { "fetch_from": "bank_account.bank_account_no", "fieldname": "bank_account_no", "fieldtype": "Read Only", - "label": "Bank Account No", - "show_days": 1, - "show_seconds": 1 + "label": "Bank Account No" }, { "fieldname": "payment_order", @@ -636,16 +520,12 @@ "no_copy": 1, "options": "Payment Order", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section", - "show_days": 1, - "show_seconds": 1 + "label": "Subscription Section" }, { "allow_on_submit": 1, @@ -655,9 +535,7 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "amended_from", @@ -666,9 +544,7 @@ "no_copy": 1, "options": "Payment Entry", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "title", @@ -683,18 +559,14 @@ "fieldname": "bank_account", "fieldtype": "Link", "label": "Company Bank Account", - "options": "Bank Account", - "show_days": 1, - "show_seconds": 1 + "options": "Bank Account" }, { "depends_on": "party", "fieldname": "party_bank_account", "fieldtype": "Link", "label": "Party Bank Account", - "options": "Bank Account", - "show_days": 1, - "show_seconds": 1 + "options": "Bank Account" }, { "fieldname": "payment_order_status", @@ -702,23 +574,17 @@ "hidden": 1, "label": "Payment Order Status", "options": "Initiated\nPayment Ordered", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", - "label": "Accounting Dimensions", - "show_days": 1, - "show_seconds": 1 + "label": "Accounting Dimensions" }, { "fieldname": "dimension_col_break", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "default": "Draft", @@ -739,70 +605,55 @@ "fieldname": "tax_withholding_category", "fieldtype": "Link", "label": "Tax Withholding Category", - "options": "Tax Withholding Category", - "show_days": 1, - "show_seconds": 1 + "mandatory_depends_on": "eval:doc.apply_tax_withholding_amount", + "options": "Tax Withholding Category" }, { "default": "0", "depends_on": "eval:doc.party_type == 'Supplier'", "fieldname": "apply_tax_withholding_amount", "fieldtype": "Check", - "label": "Apply Tax Withholding Amount", - "show_days": 1, - "show_seconds": 1 + "label": "Apply Tax Withholding Amount" }, { "collapsible": 1, "fieldname": "taxes_and_charges_section", "fieldtype": "Section Break", - "label": "Taxes and Charges", - "show_days": 1, - "show_seconds": 1 + "label": "Taxes and Charges" }, { "depends_on": "eval:doc.party_type == 'Supplier'", "fieldname": "purchase_taxes_and_charges_template", "fieldtype": "Link", "label": "Taxes and Charges Template", - "options": "Purchase Taxes and Charges Template", - "show_days": 1, - "show_seconds": 1 + "options": "Purchase Taxes and Charges Template" }, { "depends_on": "eval: doc.party_type == 'Customer'", "fieldname": "sales_taxes_and_charges_template", "fieldtype": "Link", "label": "Taxes and Charges Template", - "options": "Sales Taxes and Charges Template", - "show_days": 1, - "show_seconds": 1 + "options": "Sales Taxes and Charges Template" }, { "depends_on": "eval: doc.party_type == 'Supplier' || doc.party_type == 'Customer'", "fieldname": "taxes", "fieldtype": "Table", "label": "Advance Taxes and Charges", - "options": "Advance Taxes and Charges", - "show_days": 1, - "show_seconds": 1 + "options": "Advance Taxes and Charges" }, { "fieldname": "base_total_taxes_and_charges", "fieldtype": "Currency", "label": "Total Taxes and Charges (Company Currency)", "options": "Company:company:default_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "total_taxes_and_charges", "fieldtype": "Currency", "label": "Total Taxes and Charges", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "paid_amount_after_tax", @@ -810,61 +661,47 @@ "hidden": 1, "label": "Paid Amount After Tax", "options": "paid_from_account_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_paid_amount_after_tax", "fieldtype": "Currency", "label": "Paid Amount After Tax (Company Currency)", "options": "Company:company:default_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_55", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "section_break_56", "fieldtype": "Section Break", - "hide_border": 1, - "show_days": 1, - "show_seconds": 1 + "hide_border": 1 }, { "fieldname": "advance_tax_account", "fieldtype": "Link", "label": "Advance Tax Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "received_amount_after_tax", "fieldtype": "Currency", "label": "Received Amount After Tax", - "options": "paid_to_account_currency", - "show_days": 1, - "show_seconds": 1 + "options": "paid_to_account_currency" }, { "fieldname": "base_received_amount_after_tax", "fieldtype": "Currency", "label": "Received Amount After Tax (Company Currency)", - "options": "Company:company:default_currency", - "show_days": 1, - "show_seconds": 1 + "options": "Company:company:default_currency" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-05-15 13:05:16.958866", + "modified": "2021-05-19 02:33:08.192932", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", @@ -908,4 +745,4 @@ "sort_order": "DESC", "title_field": "title", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index ac8e7070f5..cbe8045fd1 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -436,7 +436,10 @@ class PaymentEntry(AccountsController): if not tax_withholding_details: return - tax_withholding_details.update({'included_in_paid_amount': included_in_paid_amount}) + tax_withholding_details.update({ + 'included_in_paid_amount': included_in_paid_amount, + 'cost_center': self.cost_center or erpnext.get_default_cost_center(self.company) + }) accounts = [] for d in self.taxes: @@ -1412,6 +1415,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= if doc.doctype == 'Purchase Order' and doc.apply_tds: pe.apply_tax_withholding_amount = 1 + pe.tax_withholding_category = doc.tax_withholding_category return pe diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index f925bcf5ba..934c731cf1 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -900,47 +900,6 @@ class PurchaseInvoice(BuyingController): "cost_center": self.cost_center }, account_currency, item=self)) - def allocate_advance_taxes(self, gl_entries): - tax_map = self.get_tax_map() - for pe in self.get('advances'): - if pe.reference_type == 'Payment Entry': - pe = frappe.get_doc('Payment Entry', pe.reference_name) - for tax in pe.get('taxes'): - account_currency = get_account_currency(tax.account_head) - dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" - rev_dr_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" - - unallocated_amount = tax.tax_amount - tax.allocated_amount - if tax_map.get(tax.account_head): - amount = tax_map.get(tax.account_head) - if amount < unallocated_amount: - unallocated_amount = amount - - gl_entries.append( - self.get_gl_dict({ - "account": tax.account_head, - "against": self.supplier, - dr_or_cr: unallocated_amount, - dr_or_cr + "_in_account_currency": unallocated_amount - if account_currency==self.company_currency - else unallocated_amount, - 'cost_center': tax.cost_center - }, account_currency, item=tax)) - - gl_entries.append( - self.get_gl_dict({ - "account": pe.advance_tax_account, - "against": self.supplier, - rev_dr_cr: unallocated_amount, - rev_dr_cr + "_in_account_currency": unallocated_amount - if account_currency==self.company_currency - else unallocated_amount, - 'cost_center': tax.cost_center or self.cost_center - }, account_currency, item=tax)) - - frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount + unallocated_amount) - tax_map[tax.account_head] -= unallocated_amount - def make_payment_gl_entries(self, gl_entries): # Make Cash GL Entries if cint(self.is_paid) and self.cash_bank_account and self.paid_amount: diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 66be11ff23..0da46e9886 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -950,6 +950,96 @@ class TestPurchaseInvoice(unittest.TestCase): acc_settings.submit_journal_entriessubmit_journal_entries = 0 acc_settings.save() + def test_purchase_invoice_advance_taxes(self): + from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order + from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry + from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_purchase_invoice + + # Update tax withholding category with current fiscal year and rate details + update_tax_witholding_category('_Test Company', 'TDS Payable - _TC', nowdate()) + + # Create Purchase Order with TDS applied + po = create_purchase_order(do_not_save=1, rate=3000) + po.apply_tds = 1 + po.tax_withholding_category = 'TDS - 194 - Dividends - Individual' + po.save() + po.submit() + + # Update Unrealized Profit / Loss Account which is used as default advance tax account + frappe.db.set_value('Company', '_Test Company', 'unrealized_profit_loss_account', '_Test Account Excise Duty - _TC') + + # Create Payment Entry Against the order + payment_entry = get_payment_entry(dt='Purchase Order', dn=po.name) + payment_entry.save() + payment_entry.submit() + + # Check GLE for Payment Entry + expected_gle = [ + ['_Test Account Excise Duty - _TC', 6000, 0], + ['Cash - _TC', 0, 24000], + ['Creditors - _TC', 24000, 0], + ['TDS Payable - _TC', 0, 6000], + ] + + gl_entries = frappe.db.sql("""select account, debit, credit + from `tabGL Entry` + where voucher_type='Payment Entry' and voucher_no=%s + order by account asc""", (payment_entry.name), as_dict=1) + + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_gle[i][0], gle.account) + self.assertEqual(expected_gle[i][1], gle.debit) + self.assertEqual(expected_gle[i][2], gle.credit) + + # Create Purchase Invoice against Purchase Order + purchase_invoice = get_mapped_purchase_invoice(po.name) + purchase_invoice.allocate_advances_automatically = 1 + purchase_invoice.save() + purchase_invoice.submit() + + # Check GLE for Purchase Invoice + # Zero net effect on final TDS Payable on invoice + expected_gle = [ + ['_Test Account Excise Duty - _TC', 0, 6000], + ['Cost of Goods Sold - _TC', 30000, 0], + ['Creditors - _TC', 0, 24000], + ['TDS Payable - _TC', 6000, 6000], + ] + + gl_entries = frappe.db.sql("""select account, debit, credit + from `tabGL Entry` + where voucher_type='Purchase Invoice' and voucher_no=%s + order by account asc""", (purchase_invoice.name), as_dict=1) + + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_gle[i][0], gle.account) + self.assertEqual(expected_gle[i][1], gle.debit) + self.assertEqual(expected_gle[i][2], gle.credit) + +def update_tax_witholding_category(company, account, date): + from erpnext.accounts.utils import get_fiscal_year + + fiscal_year = get_fiscal_year(date=date, company=company) + + if not frappe.db.get_value('Tax Withholding Rate', + {'parent': 'TDS - 194 - Dividends - Individual', 'fiscal_year': fiscal_year[0]}): + tds_category = frappe.get_doc('Tax Withholding Category', 'TDS - 194 - Dividends - Individual') + tds_category.append('rates', { + 'fiscal_year': fiscal_year[0], + 'tax_withholding_rate': 20, + 'single_threshold': 2500, + 'cumulative_threshold': 0 + }) + tds_category.save() + + if not frappe.db.get_value('Tax Withholding Account', + {'parent': 'TDS - 194 - Dividends - Individual', 'account': account}): + tds_category = frappe.get_doc('Tax Withholding Category', 'TDS - 194 - Dividends - Individual') + tds_category.append('accounts', { + 'company': company, + 'account': account + }) + tds_category.save() def unlink_payment_on_cancel_of_invoice(enable=1): accounts_settings = frappe.get_doc("Accounts Settings") diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 664b3ab0f2..485ae18bf0 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -913,47 +913,6 @@ class SalesInvoice(SellingController): "cost_center": self.cost_center }, account_currency, item=self)) - def allocate_advance_taxes(self, gl_entries): - tax_map = self.get_tax_map() - for pe in self.get('advances'): - if pe.reference_type == 'Payment Entry': - pe = frappe.get_doc('Payment Entry', pe.reference_name) - for tax in pe.get('taxes'): - account_currency = get_account_currency(tax.account_head) - dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" - rev_dr_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" - - unallocated_amount = tax.tax_amount - tax.allocated_amount - if tax_map.get(tax.account_head): - amount = tax_map.get(tax.account_head) - if amount < unallocated_amount: - unallocated_amount = amount - - gl_entries.append( - self.get_gl_dict({ - "account": tax.account_head, - "against": self.customer, - dr_or_cr: unallocated_amount, - dr_or_cr + "_in_account_currency": unallocated_amount - if account_currency==self.company_currency - else unallocated_amount, - 'cost_center': tax.cost_center - }, account_currency, item=tax)) - - gl_entries.append( - self.get_gl_dict({ - "account": pe.advance_tax_account, - "against": self.customer, - rev_dr_cr: unallocated_amount, - rev_dr_cr + "_in_account_currency": unallocated_amount - if account_currency==self.company_currency - else unallocated_amount, - 'cost_center': tax.cost_center - }, account_currency, item=tax)) - - frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount + unallocated_amount) - tax_map[tax.account_head] -= unallocated_amount - def make_item_gl_entries(self, gl_entries): # income account gl entries for item in self.get("items"): diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 11a9d90a1f..1e85433a60 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -700,7 +700,7 @@ class AccountsController(TransactionBase): from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries if self.doctype in ["Sales Invoice", "Purchase Invoice"]: - self.update_allocated_advance_taxes() + self.update_allocated_advance_taxes_on_cancel() if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'): unlink_ref_doc_from_payment_entries(self) @@ -716,7 +716,7 @@ class AccountsController(TransactionBase): return tax_map - def update_allocated_advance_taxes(self): + def update_allocated_advance_taxes_on_cancel(self): if self.get('advances'): tax_accounts = [d.account_head for d in self.get('taxes')] allocated_tax_map = frappe._dict(frappe.get_all('GL Entry', fields=['account', 'sum(credit - debit)'], @@ -734,10 +734,60 @@ class AccountsController(TransactionBase): allocated_amount = tax.tax_amount if allocated_amount: - frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount - allocated_amount) + frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', + tax.allocated_amount - allocated_amount) tax_map[tax.account_head] -= allocated_amount allocated_tax_map[tax.account_head] -= allocated_amount + def allocate_advance_taxes(self, gl_entries): + tax_map = self.get_tax_map() + for pe in self.get("advances"): + if pe.reference_type == "Payment Entry": + pe = frappe.get_doc("Payment Entry", pe.reference_name) + for tax in pe.get("taxes"): + account_currency = get_account_currency(tax.account_head) + + if self.doctype == "Purchase Invoice": + dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" + rev_dr_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" + else: + dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" + rev_dr_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" + + party = self.supplier if self.doctype == "Purchase Invoice" else self.customer + unallocated_amount = tax.tax_amount - tax.allocated_amount + if tax_map.get(tax.account_head): + amount = tax_map.get(tax.account_head) + if amount < unallocated_amount: + unallocated_amount = amount + + gl_entries.append( + self.get_gl_dict({ + "account": tax.account_head, + "against": party, + dr_or_cr: unallocated_amount, + dr_or_cr + "_in_account_currency": unallocated_amount + if account_currency==self.company_currency + else unallocated_amount, + "cost_center": tax.cost_center + }, account_currency, item=tax)) + + gl_entries.append( + self.get_gl_dict({ + "account": pe.advance_tax_account, + "against": party, + rev_dr_cr: unallocated_amount, + rev_dr_cr + "_in_account_currency": unallocated_amount + if account_currency==self.company_currency + else unallocated_amount, + "cost_center": tax.cost_center + }, account_currency, item=tax)) + + frappe.db.set_value("Advance Taxes and Charges", tax.name, "allocated_amount", + tax.allocated_amount + unallocated_amount) + + tax_map[tax.account_head] -= unallocated_amount + def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield): from erpnext.controllers.status_updater import get_allowance_for item_allowance = {} From b07f7d1b70352a6a85549541fc4a635df6ff393a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 20 May 2021 17:08:57 +0530 Subject: [PATCH 051/180] fix: Linting and other fixes --- .../doctype/payment_entry/payment_entry.js | 19 ++++++--- .../doctype/payment_entry/payment_entry.json | 4 +- .../doctype/payment_entry/payment_entry.py | 19 ++++++--- .../purchase_invoice/test_purchase_invoice.py | 40 +++++++++---------- .../doctype/purchase_order/purchase_order.js | 3 +- erpnext/public/js/payment/payments.js | 33 +++++++-------- 6 files changed, 66 insertions(+), 52 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 19d73b1ad4..a7fcbd7e3b 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -1112,12 +1112,16 @@ frappe.ui.form.on('Payment Entry', { current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; + let applicable_tax_amount = 0 + if (!tax.included_in_paid_amount) { - if(i==0) { - tax.total = flt(frm.doc.paid_amount + current_tax_amount, precision("total", tax)); - } else { - tax.total = flt(frm.doc["taxes"][i-1].total + current_tax_amount, precision("total", tax)); - } + applicable_tax_amount = current_tax_amount + } + + if(i==0) { + tax.total = flt(frm.doc.paid_amount + applicable_tax_amount, precision("total", tax)); + } else { + tax.total = flt(frm.doc["taxes"][i-1].total + applicable_tax_amount, precision("total", tax)); } tax.base_total = tax.total * frm.doc.source_exchange_rate; @@ -1194,6 +1198,11 @@ frappe.ui.form.on('Advance Taxes and Charges', { taxes_remove: function(frm) { frm.events.calculate_taxes(frm); frm.events.set_unallocated_amount(frm); + }, + + included_in_paid_amount: function(frm) { + frm.events.calculate_taxes(frm); + frm.events.set_unallocated_amount(frm); } }) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index b4b92f926e..1e2bd8194a 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -29,7 +29,6 @@ "paid_from", "paid_from_account_currency", "paid_from_account_balance", - "advance_tax_account", "column_break_18", "paid_to", "paid_to_account_currency", @@ -60,6 +59,7 @@ "taxes_and_charges_section", "purchase_taxes_and_charges_template", "sales_taxes_and_charges_template", + "advance_tax_account", "column_break_55", "apply_tax_withholding_amount", "tax_withholding_category", @@ -701,7 +701,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-05-19 02:33:08.192932", + "modified": "2021-05-20 02:04:56.766124", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index cbe8045fd1..d960156670 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -444,6 +444,11 @@ class PaymentEntry(AccountsController): accounts = [] for d in self.taxes: if d.account_head == tax_withholding_details.get("account_head"): + + # Preserve user updated included in paid amount + if d.included_in_paid_amount: + tax_withholding_details.update({'included_in_paid_amount': d.included_in_paid_amount}) + d.update(tax_withholding_details) accounts.append(d.account_head) @@ -839,12 +844,16 @@ class PaymentEntry(AccountsController): current_tax_amount *= 1.0 if not tax.included_in_paid_amount: - if i == 0: - tax.total = flt(self.paid_amount + current_tax_amount, self.precision("total", tax)) - else: - tax.total = flt(self.get('taxes')[i-1].total + current_tax_amount, self.precision("total", tax)) + applicable_tax = current_tax_amount + else: + applicable_tax = 0 - tax.base_total = tax.total * self.source_exchange_rate + if i == 0: + tax.total = flt(self.paid_amount + applicable_tax, self.precision("total", tax)) + else: + tax.total = flt(self.get('taxes')[i-1].total + applicable_tax, self.precision("total", tax)) + + tax.base_total = tax.total * self.source_exchange_rate self.total_taxes_and_charges += current_tax_amount self.base_total_taxes_and_charges += current_tax_amount * self.source_exchange_rate diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 0da46e9886..aa56ddfdf3 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -992,29 +992,29 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(expected_gle[i][2], gle.credit) # Create Purchase Invoice against Purchase Order - purchase_invoice = get_mapped_purchase_invoice(po.name) - purchase_invoice.allocate_advances_automatically = 1 - purchase_invoice.save() - purchase_invoice.submit() + # purchase_invoice = get_mapped_purchase_invoice(po.name) + # purchase_invoice.allocate_advances_automatically = 1 + # purchase_invoice.save() + # purchase_invoice.submit() - # Check GLE for Purchase Invoice - # Zero net effect on final TDS Payable on invoice - expected_gle = [ - ['_Test Account Excise Duty - _TC', 0, 6000], - ['Cost of Goods Sold - _TC', 30000, 0], - ['Creditors - _TC', 0, 24000], - ['TDS Payable - _TC', 6000, 6000], - ] + # # Check GLE for Purchase Invoice + # # Zero net effect on final TDS Payable on invoice + # expected_gle = [ + # ['_Test Account Excise Duty - _TC', 0, 6000], + # ['Cost of Goods Sold - _TC', 30000, 0], + # ['Creditors - _TC', 0, 24000], + # ['TDS Payable - _TC', 6000, 6000], + # ] - gl_entries = frappe.db.sql("""select account, debit, credit - from `tabGL Entry` - where voucher_type='Purchase Invoice' and voucher_no=%s - order by account asc""", (purchase_invoice.name), as_dict=1) + # gl_entries = frappe.db.sql("""select account, debit, credit + # from `tabGL Entry` + # where voucher_type='Purchase Invoice' and voucher_no=%s + # order by account asc""", (purchase_invoice.name), as_dict=1) - for i, gle in enumerate(gl_entries): - self.assertEqual(expected_gle[i][0], gle.account) - self.assertEqual(expected_gle[i][1], gle.debit) - self.assertEqual(expected_gle[i][2], gle.credit) + # for i, gle in enumerate(gl_entries): + # self.assertEqual(expected_gle[i][0], gle.account) + # self.assertEqual(expected_gle[i][1], gle.debit) + # self.assertEqual(expected_gle[i][2], gle.credit) def update_tax_witholding_category(company, account, date): from erpnext.accounts.utils import get_fiscal_year diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index ccb3ce33ec..0f6d927b36 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -321,7 +321,8 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( if(me.values) { me.values.sub_con_rm_items.map((row,i) => { if (!row.item_code || !row.rm_item_code || !row.warehouse || !row.qty || row.qty === 0) { - frappe.throw(__("Item Code and warehouse and quantity are required on row {0}", [i+1])); + let row_id = i+1; + frappe.throw(__("Item Code, warehouse and quantity are required on row {0}", [row_id])); } }) me._make_rm_stock_entry(me.dialog.fields_dict.sub_con_rm_items.grid.get_selected_children()) diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js index 9fb2f3ee7f..b9e933a38e 100644 --- a/erpnext/public/js/payment/payments.js +++ b/erpnext/public/js/payment/payments.js @@ -16,12 +16,11 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.select_text(); }, - select_text: function() { - var me = this; + select_text() { $(this.$body).find('.form-control').click(function() { $(this).select(); - }) - }, + }); + } set_payment_primary_action: function() { var me = this; @@ -62,8 +61,8 @@ erpnext.payments = erpnext.stock.StockController.extend({ 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){ + 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, @@ -88,12 +87,12 @@ erpnext.payments = erpnext.stock.StockController.extend({ 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){ + 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){ + 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()); } @@ -101,8 +100,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.bind_amount_change_event(); }, - bind_keyboard_event: function() { - var me = this; + bind_keyboard_event() { this.payment_val = ''; this.bind_form_control_event(); this.bind_numeric_keys_event(); @@ -130,8 +128,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ }); }, - highlight_selected_row: function() { - var me = this; + highlight_selected_row() { 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'); @@ -157,7 +154,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ }, - bind_amount_change_event: function() { + bind_amount_change_event() { var me = this; this.selected_mode.change(function() { me.payment_val = flt($(this).val()) || 0.0; @@ -180,9 +177,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ }); }, - write_off_amount: function(write_off_amount) { - var me = this; - + write_off_amount(write_off_amount) { 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")); @@ -204,7 +199,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ var value = me.selected_mode.val(); if (me.idx == 'change_amount') { me.change_amount(value); - } else{ + } 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)); } @@ -219,7 +214,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ var me = this; $.each(this.frm.doc.payments, function(index, data) { - if(cint(me.idx) == cint(data.idx)){ + if (cint(me.idx) == cint(data.idx)) { data.amount = flt(me.selected_mode.val(), 2); } }) From 6f84cee6feb1f2160bd1b48a6e4f45f8ee9cf952 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 21 May 2021 11:15:18 +0530 Subject: [PATCH 052/180] fix: Uncommentt test --- .../purchase_invoice/test_purchase_invoice.py | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index aa56ddfdf3..e71a416f3d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -991,30 +991,30 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(expected_gle[i][1], gle.debit) self.assertEqual(expected_gle[i][2], gle.credit) - # Create Purchase Invoice against Purchase Order - # purchase_invoice = get_mapped_purchase_invoice(po.name) - # purchase_invoice.allocate_advances_automatically = 1 - # purchase_invoice.save() - # purchase_invoice.submit() + Create Purchase Invoice against Purchase Order + purchase_invoice = get_mapped_purchase_invoice(po.name) + purchase_invoice.allocate_advances_automatically = 1 + purchase_invoice.save() + purchase_invoice.submit() - # # Check GLE for Purchase Invoice - # # Zero net effect on final TDS Payable on invoice - # expected_gle = [ - # ['_Test Account Excise Duty - _TC', 0, 6000], - # ['Cost of Goods Sold - _TC', 30000, 0], - # ['Creditors - _TC', 0, 24000], - # ['TDS Payable - _TC', 6000, 6000], - # ] + # Check GLE for Purchase Invoice + # Zero net effect on final TDS Payable on invoice + expected_gle = [ + ['_Test Account Excise Duty - _TC', 0, 6000], + ['Cost of Goods Sold - _TC', 30000, 0], + ['Creditors - _TC', 0, 24000], + ['TDS Payable - _TC', 6000, 6000], + ] - # gl_entries = frappe.db.sql("""select account, debit, credit - # from `tabGL Entry` - # where voucher_type='Purchase Invoice' and voucher_no=%s - # order by account asc""", (purchase_invoice.name), as_dict=1) + gl_entries = frappe.db.sql("""select account, debit, credit + from `tabGL Entry` + where voucher_type='Purchase Invoice' and voucher_no=%s + order by account asc""", (purchase_invoice.name), as_dict=1) - # for i, gle in enumerate(gl_entries): - # self.assertEqual(expected_gle[i][0], gle.account) - # self.assertEqual(expected_gle[i][1], gle.debit) - # self.assertEqual(expected_gle[i][2], gle.credit) + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_gle[i][0], gle.account) + self.assertEqual(expected_gle[i][1], gle.debit) + self.assertEqual(expected_gle[i][2], gle.credit) def update_tax_witholding_category(company, account, date): from erpnext.accounts.utils import get_fiscal_year From 36d47f97f400630ffaa259e7dc50fe7b120e0c35 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 21 May 2021 11:16:26 +0530 Subject: [PATCH 053/180] fix: Linting issues --- erpnext/public/js/payment/payments.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js index b9e933a38e..ddf8706809 100644 --- a/erpnext/public/js/payment/payments.js +++ b/erpnext/public/js/payment/payments.js @@ -20,7 +20,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ $(this.$body).find('.form-control').click(function() { $(this).select(); }); - } + }, set_payment_primary_action: function() { var me = this; @@ -85,8 +85,8 @@ erpnext.payments = erpnext.stock.StockController.extend({ 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 + 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")) From 55a3fb57dd213993907114615bb3c406a191d9e7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 22 May 2021 20:30:18 +0530 Subject: [PATCH 054/180] fix: Linting Issues --- .../accounts/doctype/purchase_invoice/test_purchase_invoice.py | 2 +- .../accounts/report/tds_payable_monthly/tds_payable_monthly.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index e71a416f3d..0da46e9886 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -991,7 +991,7 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(expected_gle[i][1], gle.debit) self.assertEqual(expected_gle[i][2], gle.credit) - Create Purchase Invoice against Purchase Order + # Create Purchase Invoice against Purchase Order purchase_invoice = get_mapped_purchase_invoice(po.name) purchase_invoice.allocate_advances_automatically = 1 purchase_invoice.save() diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index 619d799d06..ceefa31cfa 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -56,7 +56,6 @@ def set_filters(filters): elif filters.purchase_order and not filters.supplier: for d in filters.get("invoices"): if d.name == filters.purchase_order: - print("$#$#$$#") invoices.append(d) filters["invoices"] = invoices if invoices else filters["invoices"] From 73e41c0bd6e7984c468ca5c3feaac0d5920110d2 Mon Sep 17 00:00:00 2001 From: noahjacob Date: Tue, 25 May 2021 18:01:47 +0530 Subject: [PATCH 055/180] refactor: suggested changes --- erpnext/manufacturing/doctype/work_order/work_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index a154464a8b..2600790a59 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -383,7 +383,7 @@ class WorkOrder(Document): work_order_qty = 0.0 if plan_reference.item_reference == item_reference: if self.docstatus == 1: - work_order_qty = cint(plan_reference.qty) / total_bundle_qty + work_order_qty = flt(plan_reference.qty) / total_bundle_qty frappe.db.set_value('Sales Order Item', plan_reference.sales_order_item, 'work_order_qty', work_order_qty) From 84f270e73223313dc789bb5617911ded5efbc0a7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 25 May 2021 18:03:42 +0530 Subject: [PATCH 056/180] fix: Remove unwanted commits --- erpnext/controllers/taxes_and_totals.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index a6b54eb590..0b89750766 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -819,16 +819,3 @@ class init_landed_taxes_and_totals(object): for d in self.doc.get(self.tax_field): d.amount = flt(d.amount, d.precision("amount")) d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount")) - -def get_advance_taxes(payment_entry_list): - taxes = [] - if payment_entry_list: - taxes = frappe.db.sql( - """ - SELECT t.parent, t.add_deduct_tax, t.charge_type, t.rate, - t.account_head, t.cost_center, t.tax_amount, t.description - FROM `tabAdvance Taxes and Charges` t, `tabPayment Entry` p - WHERE t.parent = p.name AND t.parent in %s - """, (payment_entry_list, ), as_dict=1) - - return taxes From 8fddd0f0c1a4b4f51af8e7834e37d7f48a08576a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 25 May 2021 22:03:50 +0530 Subject: [PATCH 057/180] fix: Update TDS rate for test --- .../doctype/purchase_invoice/test_purchase_invoice.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 0da46e9886..319808ac95 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -975,10 +975,10 @@ class TestPurchaseInvoice(unittest.TestCase): # Check GLE for Payment Entry expected_gle = [ - ['_Test Account Excise Duty - _TC', 6000, 0], + ['_Test Account Excise Duty - _TC', 3000, 0], ['Cash - _TC', 0, 24000], ['Creditors - _TC', 24000, 0], - ['TDS Payable - _TC', 0, 6000], + ['TDS Payable - _TC', 0, 3000], ] gl_entries = frappe.db.sql("""select account, debit, credit @@ -1000,10 +1000,10 @@ class TestPurchaseInvoice(unittest.TestCase): # Check GLE for Purchase Invoice # Zero net effect on final TDS Payable on invoice expected_gle = [ - ['_Test Account Excise Duty - _TC', 0, 6000], + ['_Test Account Excise Duty - _TC', 0, 3000], ['Cost of Goods Sold - _TC', 30000, 0], ['Creditors - _TC', 0, 24000], - ['TDS Payable - _TC', 6000, 6000], + ['TDS Payable - _TC', 6000, 3000], ] gl_entries = frappe.db.sql("""select account, debit, credit @@ -1026,7 +1026,7 @@ def update_tax_witholding_category(company, account, date): tds_category = frappe.get_doc('Tax Withholding Category', 'TDS - 194 - Dividends - Individual') tds_category.append('rates', { 'fiscal_year': fiscal_year[0], - 'tax_withholding_rate': 20, + 'tax_withholding_rate': 10, 'single_threshold': 2500, 'cumulative_threshold': 0 }) From 5e27c7dae21c317bde812547cc6ae1f6c69ba61d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 25 May 2021 22:34:58 +0530 Subject: [PATCH 058/180] fix: Add non group filter for account --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index a7fcbd7e3b..a4022ef40a 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -97,7 +97,8 @@ frappe.ui.form.on('Payment Entry', { return { filters: { "company": frm.doc.company, - "root_type": ["in", ["Asset", "Liability"]] + "root_type": ["in", ["Asset", "Liability"]], + "is_group": 0 } } }); From 1cdf5a0dbad7eea6111839bfc14e4b59f8970f59 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Wed, 26 May 2021 14:42:15 +0530 Subject: [PATCH 059/180] fix: add requested changes --- erpnext/controllers/stock_controller.py | 8 +- erpnext/public/js/controllers/transaction.js | 15 +- .../stock/doctype/stock_entry/stock_entry.js | 130 ++++++++++++++++++ 3 files changed, 144 insertions(+), 9 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 6f97743b60..abc966a477 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -508,7 +508,7 @@ def make_quality_inspections(doctype, docname, items): inspections = [] for item in items: - if item.get("sample_size") > item.get("qty"): + if flt(item.get("sample_size")) > flt(item.get("qty")): frappe.throw(_("{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity})").format( item_name=item.get("item_name"), sample_size=item.get("sample_size"), @@ -523,14 +523,14 @@ def make_quality_inspections(doctype, docname, items): "reference_name": docname, "item_code": item.get("item_code"), "description": item.get("description"), - "sample_size": item.get("sample_size"), + "sample_size": flt(item.get("sample_size")), "item_serial_no": item.get("serial_no").split("\n")[0] if item.get("serial_no") else None, "batch_no": item.get("batch_no") }).insert() quality_inspection.save() - inspections.append(quality_inspection) + inspections.append(quality_inspection.name) - return [get_link_to_form("Quality Inspection", inspection.name) for inspection in inspections] + return inspections def is_reposting_pending(): diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 8738957166..95562baa8e 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -2039,11 +2039,16 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, freeze: true, callback: function (r) { - if (r.message) { - frappe.msgprint({ - message: __("Quality Inspections Created: {0}", [r.message.join(", ")]), - indicator: "green" - }); + if (r.message.length > 0) { + if (r.message.length === 1) { + frappe.set_route("Form", "Quality Inspection", r.message[0]); + } else { + frappe.route_options = { + "reference_type": me.frm.doc.doctype, + "reference_name": me.frm.doc.name + }; + frappe.set_route("List", "Quality Inspection"); + } } dialog.hide(); } diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index de23e769f8..3524c41720 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -115,6 +115,13 @@ frappe.ui.form.on('Stock Entry', { return; } + if (!frm.is_new() && frm.doc.docstatus === 0) { + frm.add_custom_button(__("Quality Inspection(s)"), () => { + frm.trigger("make_quality_inspection"); + }, __("Create")); + frm.page.set_inner_btn_group_as_primary(__('Create')); + } + let quality_inspection_field = frm.get_docfield("items", "quality_inspection"); quality_inspection_field.get_route_options_for_new_doc = function(row) { if (frm.is_new()) return; @@ -142,6 +149,129 @@ frappe.ui.form.on('Stock Entry', { }); }, + make_quality_inspection: function (frm) { + let data = []; + const fields = [ + { + label: "Items", + fieldtype: "Table", + fieldname: "items", + cannot_add_rows: true, + in_place_edit: true, + data: data, + get_data: () => { + return data; + }, + fields: [ + { + fieldtype: "Data", + fieldname: "docname", + hidden: true + }, + { + fieldtype: "Read Only", + fieldname: "item_code", + label: __("Item Code"), + in_list_view: true + }, + { + fieldtype: "Read Only", + fieldname: "item_name", + label: __("Item Name"), + in_list_view: true + }, + { + fieldtype: "Float", + fieldname: "qty", + label: __("Accepted Quantity"), + in_list_view: true, + read_only: true + }, + { + fieldtype: "Float", + fieldname: "sample_size", + label: __("Sample Size"), + reqd: true, + in_list_view: true + }, + { + fieldtype: "Data", + fieldname: "description", + label: __("Description"), + hidden: true + }, + { + fieldtype: "Data", + fieldname: "serial_no", + label: __("Serial No"), + hidden: true + }, + { + fieldtype: "Data", + fieldname: "batch_no", + label: __("Batch No"), + hidden: true + } + ] + } + ]; + + const dialog = new frappe.ui.Dialog({ + title: __("Select Items for Quality Inspection"), + fields: fields, + primary_action: function () { + const data = dialog.get_values(); + frappe.call({ + method: "erpnext.controllers.stock_controller.make_quality_inspections", + args: { + doctype: frm.doc.doctype, + docname: frm.doc.name, + items: data + }, + freeze: true, + callback: function (r) { + if (r.message.length > 0) { + if (r.message.length === 1) { + frappe.set_route("Form", "Quality Inspection", r.message[0]); + } else { + frappe.route_options = { + "reference_type": frm.doc.doctype, + "reference_name": frm.doc.name + }; + frappe.set_route("List", "Quality Inspection"); + } + } + dialog.hide(); + } + }); + }, + primary_action_label: __("Create") + }); + + frm.doc.items.forEach(item => { + if (!item.quality_inspection) { + let dialog_items = dialog.fields_dict.items; + dialog_items.df.data.push({ + "docname": item.name, + "item_code": item.item_code, + "item_name": item.item_name, + "qty": item.qty, + "description": item.description, + "serial_no": item.serial_no, + "batch_no": item.batch_no + }); + dialog_items.grid.refresh(); + } + }); + + data = dialog.fields_dict.items.df.data; + if (!data.length) { + frappe.msgprint(__("All items in this document already have a linked Quality Inspection.")); + } else { + dialog.show(); + } + }, + outgoing_stock_entry: function(frm) { frappe.call({ doc: frm.doc, From 1e3a3b27c6e4f66aa38c9adaf86b5bde39293a00 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Wed, 26 May 2021 15:18:10 +0530 Subject: [PATCH 060/180] style: semgrep issues --- 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 2c3519b04a..c77ea55775 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -225,7 +225,7 @@ class AccountsController(TransactionBase): def validate_date_with_fiscal_year(self): if self.meta.get_field("fiscal_year"): - date_field = "" + date_field = None if self.meta.get_field("posting_date"): date_field = "posting_date" elif self.meta.get_field("transaction_date"): From 9857d63523d3bffc1377622cbca2ff1c4c0815d0 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Wed, 26 May 2021 15:41:36 +0530 Subject: [PATCH 061/180] fix: add requested changes --- .../stock/doctype/stock_entry/stock_entry.js | 130 +----------------- 1 file changed, 4 insertions(+), 126 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 3524c41720..93a6fc0e0a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -117,7 +117,8 @@ frappe.ui.form.on('Stock Entry', { if (!frm.is_new() && frm.doc.docstatus === 0) { frm.add_custom_button(__("Quality Inspection(s)"), () => { - frm.trigger("make_quality_inspection"); + let transaction_controller = new erpnext.TransactionController({ frm: frm }); + transaction_controller.make_quality_inspection(); }, __("Create")); frm.page.set_inner_btn_group_as_primary(__('Create')); } @@ -149,129 +150,6 @@ frappe.ui.form.on('Stock Entry', { }); }, - make_quality_inspection: function (frm) { - let data = []; - const fields = [ - { - label: "Items", - fieldtype: "Table", - fieldname: "items", - cannot_add_rows: true, - in_place_edit: true, - data: data, - get_data: () => { - return data; - }, - fields: [ - { - fieldtype: "Data", - fieldname: "docname", - hidden: true - }, - { - fieldtype: "Read Only", - fieldname: "item_code", - label: __("Item Code"), - in_list_view: true - }, - { - fieldtype: "Read Only", - fieldname: "item_name", - label: __("Item Name"), - in_list_view: true - }, - { - fieldtype: "Float", - fieldname: "qty", - label: __("Accepted Quantity"), - in_list_view: true, - read_only: true - }, - { - fieldtype: "Float", - fieldname: "sample_size", - label: __("Sample Size"), - reqd: true, - in_list_view: true - }, - { - fieldtype: "Data", - fieldname: "description", - label: __("Description"), - hidden: true - }, - { - fieldtype: "Data", - fieldname: "serial_no", - label: __("Serial No"), - hidden: true - }, - { - fieldtype: "Data", - fieldname: "batch_no", - label: __("Batch No"), - hidden: true - } - ] - } - ]; - - const dialog = new frappe.ui.Dialog({ - title: __("Select Items for Quality Inspection"), - fields: fields, - primary_action: function () { - const data = dialog.get_values(); - frappe.call({ - method: "erpnext.controllers.stock_controller.make_quality_inspections", - args: { - doctype: frm.doc.doctype, - docname: frm.doc.name, - items: data - }, - freeze: true, - callback: function (r) { - if (r.message.length > 0) { - if (r.message.length === 1) { - frappe.set_route("Form", "Quality Inspection", r.message[0]); - } else { - frappe.route_options = { - "reference_type": frm.doc.doctype, - "reference_name": frm.doc.name - }; - frappe.set_route("List", "Quality Inspection"); - } - } - dialog.hide(); - } - }); - }, - primary_action_label: __("Create") - }); - - frm.doc.items.forEach(item => { - if (!item.quality_inspection) { - let dialog_items = dialog.fields_dict.items; - dialog_items.df.data.push({ - "docname": item.name, - "item_code": item.item_code, - "item_name": item.item_name, - "qty": item.qty, - "description": item.description, - "serial_no": item.serial_no, - "batch_no": item.batch_no - }); - dialog_items.grid.refresh(); - } - }); - - data = dialog.fields_dict.items.df.data; - if (!data.length) { - frappe.msgprint(__("All items in this document already have a linked Quality Inspection.")); - } else { - dialog.show(); - } - }, - outgoing_stock_entry: function(frm) { frappe.call({ doc: frm.doc, @@ -285,7 +163,7 @@ frappe.ui.form.on('Stock Entry', { refresh: function(frm) { if(!frm.doc.docstatus) { frm.trigger('validate_purpose_consumption'); - frm.add_custom_button(__('Create Material Request'), function() { + frm.add_custom_button(__('Material Request'), function() { frappe.model.with_doctype('Material Request', function() { var mr = frappe.model.get_new_doc('Material Request'); var items = frm.get_field('items').grid.get_selected_children(); @@ -308,7 +186,7 @@ frappe.ui.form.on('Stock Entry', { }); frappe.set_route('Form', 'Material Request', mr.name); }); - }); + }, __("Create")); } if(frm.doc.items) { From be3671fe7b3965be2f34fb902e68d25c3f5c174b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 27 May 2021 13:27:17 +0530 Subject: [PATCH 062/180] fix: Create new supplier for test case --- .../purchase_invoice/test_purchase_invoice.py | 15 ++++++++++----- .../doctype/purchase_order/test_purchase_order.py | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 319808ac95..2610a69471 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -16,6 +16,7 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_tra from erpnext.projects.doctype.project.test_project import make_project from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account from erpnext.stock.doctype.item.test_item import create_item +from erpnext.buying.doctype.supplier.test_supplier import create_supplier test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"] test_ignore = ["Serial No"] @@ -955,11 +956,15 @@ class TestPurchaseInvoice(unittest.TestCase): from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_purchase_invoice + # create a new supplier to test + supplier = create_supplier(supplier_name = '_Test TDS Advance Supplier', + tax_withholding_category = 'TDS - 194 - Dividends - Individual') + # Update tax withholding category with current fiscal year and rate details update_tax_witholding_category('_Test Company', 'TDS Payable - _TC', nowdate()) # Create Purchase Order with TDS applied - po = create_purchase_order(do_not_save=1, rate=3000) + po = create_purchase_order(do_not_save=1, supplier=supplier.name, rate=3000) po.apply_tds = 1 po.tax_withholding_category = 'TDS - 194 - Dividends - Individual' po.save() @@ -976,8 +981,8 @@ class TestPurchaseInvoice(unittest.TestCase): # Check GLE for Payment Entry expected_gle = [ ['_Test Account Excise Duty - _TC', 3000, 0], - ['Cash - _TC', 0, 24000], - ['Creditors - _TC', 24000, 0], + ['Cash - _TC', 0, 27000], + ['Creditors - _TC', 27000, 0], ['TDS Payable - _TC', 0, 3000], ] @@ -1002,8 +1007,8 @@ class TestPurchaseInvoice(unittest.TestCase): expected_gle = [ ['_Test Account Excise Duty - _TC', 0, 3000], ['Cost of Goods Sold - _TC', 30000, 0], - ['Creditors - _TC', 0, 24000], - ['TDS Payable - _TC', 6000, 3000], + ['Creditors - _TC', 0, 27000], + ['TDS Payable - _TC', 3000, 3000], ] gl_entries = frappe.db.sql("""select account, debit, credit diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index aaa98f2f1f..39171960d8 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -1111,7 +1111,7 @@ def create_purchase_order(**args): po.schedule_date = add_days(nowdate(), 1) po.company = args.company or "_Test Company" - po.supplier = args.customer or "_Test Supplier" + po.supplier = args.supplier or "_Test Supplier" po.is_subcontracted = args.is_subcontracted or "No" po.currency = args.currency or frappe.get_cached_value('Company', po.company, "default_currency") po.conversion_factor = args.conversion_factor or 1 From ea18230274725f2cf0a3fa62b8b6f756af4dea79 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 27 May 2021 16:40:37 +0530 Subject: [PATCH 063/180] fix: Update GL Entry ordering --- .../accounts/doctype/purchase_invoice/test_purchase_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 2610a69471..07f9b42fbf 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1005,8 +1005,8 @@ class TestPurchaseInvoice(unittest.TestCase): # Check GLE for Purchase Invoice # Zero net effect on final TDS Payable on invoice expected_gle = [ + ['_Test Account Cost for Goods Sold - _TC', 30000, 0], ['_Test Account Excise Duty - _TC', 0, 3000], - ['Cost of Goods Sold - _TC', 30000, 0], ['Creditors - _TC', 0, 27000], ['TDS Payable - _TC', 3000, 3000], ] From 740c68d075c4583045052df5e3192039d2cd4576 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 27 May 2021 17:43:53 +0530 Subject: [PATCH 064/180] fix: Debug Test --- .../accounts/doctype/purchase_invoice/test_purchase_invoice.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 07f9b42fbf..8bb9d7dacf 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1016,6 +1016,9 @@ class TestPurchaseInvoice(unittest.TestCase): where voucher_type='Purchase Invoice' and voucher_no=%s order by account asc""", (purchase_invoice.name), as_dict=1) + for gle in gl_entries: + print(gle.account, gle.debit, gle.credit) + for i, gle in enumerate(gl_entries): self.assertEqual(expected_gle[i][0], gle.account) self.assertEqual(expected_gle[i][1], gle.debit) From 46d39d27aaea10eda484cf41dda0b3bb6392f67f Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 27 May 2021 14:16:01 +0200 Subject: [PATCH 065/180] fix: validate company in taxes setup --- erpnext/setup/setup_wizard/operations/taxes_setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py index 429a558c58..dd0ebd1517 100644 --- a/erpnext/setup/setup_wizard/operations/taxes_setup.py +++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py @@ -11,6 +11,9 @@ from frappe import _ def setup_taxes_and_charges(company_name: str, country: str): + if not frappe.db.exists('Company', company_name): + frappe.throw(_('Company {} does not exist yet. Taxes setup aborted.').format(company_name)) + file_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'country_wise_tax.json') with open(file_path, 'r') as json_file: tax_data = json.load(json_file) From 86ee3ebb09ea11e971a48e1718822a2e998fba10 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 27 May 2021 14:16:26 +0200 Subject: [PATCH 066/180] feat: create tax category during taxes setup --- erpnext/setup/setup_wizard/operations/taxes_setup.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py index dd0ebd1517..672caf2606 100644 --- a/erpnext/setup/setup_wizard/operations/taxes_setup.py +++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py @@ -102,6 +102,9 @@ def make_taxes_and_charges_template(company_name, doctype, template): if frappe.db.exists(doctype, {'title': template.get('title'), 'company': company_name}): return + if template.get('tax_category'): + ensure_tax_category_exists(template.get('tax_category')) + for tax_row in template.get('taxes'): account_data = tax_row.get('account_head') tax_row_defaults = { @@ -233,3 +236,10 @@ def get_or_create_tax_group(company_name, root_type): tax_group_name = tax_group_account.name return tax_group_name + + +def ensure_tax_category_exists(name): + if not frappe.db.exists('Tax Category', name): + doc = frappe.new_doc('Tax Category') + doc.title = name + doc.save() From 50794407b49469682a53b6f91dd3257ca7b25f9d Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 27 May 2021 14:17:53 +0200 Subject: [PATCH 067/180] feat: Item Tax Templates for Germany --- .../setup_wizard/data/country_wise_tax.json | 307 +++++++++++++----- 1 file changed, 232 insertions(+), 75 deletions(-) diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json index 5876488033..2e2a0ca726 100644 --- a/erpnext/setup/setup_wizard/data/country_wise_tax.json +++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json @@ -485,33 +485,32 @@ "SKR04 mit Kontonummern": { "sales_tax_templates": [ { - "title": "Umsatzsteuer 19%", + "title": "Umsatzsteuer", + "tax_category": "Umsatzsteuer", "taxes": [ { "account_head": { "account_name": "Umsatzsteuer 19%", "account_number": "3806", "tax_rate": 19.00 - } - } - ] - }, - { - "title": "Umsatzsteuer 7%", - "taxes": [ + }, + "rate": 0.00 + }, { "account_head": { "account_name": "Umsatzsteuer 7%", "account_number": "3801", "tax_rate": 7.00 - } + }, + "rate": 0.00 } ] } ], "purchase_tax_templates": [ { - "title": "Abziehbare Vorsteuer 19%", + "title": "Vorsteuer", + "tax_category": "Vorsteuer", "taxes": [ { "account_head": { @@ -519,25 +518,23 @@ "account_number": "1406", "root_type": "Asset", "tax_rate": 19.00 - } - } - ] - }, - { - "title": "Abziehbare Vorsteuer 7%", - "taxes": [ + }, + "rate": 0.00 + }, { "account_head": { "account_name": "Abziehbare Vorsteuer 7%", "account_number": "1401", "root_type": "Asset", "tax_rate": 7.00 - } + }, + "rate": 0.00 } ] }, { "title": "Innergemeinschaftlicher Erwerb 19% Umsatzsteuer und 19% Vorsteuer", + "tax_category": "Innergemeinschaftlicher Erwerb 19%", "taxes": [ { "account_head": { @@ -564,33 +561,32 @@ "SKR03 mit Kontonummern": { "sales_tax_templates": [ { - "title": "Umsatzsteuer 19%", + "title": "Umsatzsteuer", + "tax_category": "Umsatzsteuer", "taxes": [ { "account_head": { "account_name": "Umsatzsteuer 19%", "account_number": "1776", "tax_rate": 19.00 - } - } - ] - }, - { - "title": "Umsatzsteuer 7%", - "taxes": [ + }, + "rate": 0.00 + }, { "account_head": { "account_name": "Umsatzsteuer 7%", "account_number": "1771", "tax_rate": 7.00 - } + }, + "rate": 0.00 } ] } ], "purchase_tax_templates": [ { - "title": "Abziehbare Vorsteuer 19%", + "title": "Vorsteuer", + "tax_category": "Vorsteuer", "taxes": [ { "account_head": { @@ -598,20 +594,17 @@ "account_number": "1576", "root_type": "Asset", "tax_rate": 19.00 - } - } - ] - }, - { - "title": "Abziehbare Vorsteuer 7%", - "taxes": [ + }, + "rate": 0.00 + }, { "account_head": { "account_name": "Abziehbare Vorsteuer 7%", "account_number": "1571", "root_type": "Asset", "tax_rate": 7.00 - } + }, + "rate": 0.00 } ] } @@ -620,33 +613,32 @@ "Standard with Numbers": { "sales_tax_templates": [ { - "title": "Umsatzsteuer 19%", + "title": "Umsatzsteuer", + "tax_category": "Umsatzsteuer", "taxes": [ { "account_head": { "account_name": "Umsatzsteuer 19%", "account_number": "2301", "tax_rate": 19.00 - } - } - ] - }, - { - "title": "Umsatzsteuer 7%", - "taxes": [ + }, + "rate": 0.00 + }, { "account_head": { "account_name": "Umsatzsteuer 7%", "account_number": "2302", "tax_rate": 7.00 - } + }, + "rate": 0.00 } ] } ], "purchase_tax_templates": [ { - "title": "Abziehbare Vorsteuer 19%", + "title": "Vorsteuer", + "tax_category": "Vorsteuer", "taxes": [ { "account_head": { @@ -654,20 +646,107 @@ "account_number": "1501", "root_type": "Asset", "tax_rate": 19.00 - } - } - ] - }, - { - "title": "Abziehbare Vorsteuer 7%", - "taxes": [ + }, + "rate": 0.00 + }, { "account_head": { "account_name": "Abziehbare Vorsteuer 7%", "account_number": "1502", "root_type": "Asset", "tax_rate": 7.00 - } + }, + "rate": 0.00 + } + ] + } + ], + "item_tax_templates": [ + { + "title": "Umsatzsteuer 19%", + "taxes": [ + { + "tax_type": { + "account_name": "Umsatzsteuer 19%", + "account_number": "2301", + "tax_rate": 19.00 + }, + "tax_rate": 19.00 + }, + { + "tax_type": { + "account_name": "Umsatzsteuer 7%", + "account_number": "2302", + "tax_rate": 7.00 + }, + "tax_rate": 0.00 + } + ] + }, + { + "title": "Umsatzsteuer 7%", + "taxes": [ + { + "tax_type": { + "account_name": "Umsatzsteuer 19%", + "account_number": "2301", + "tax_rate": 19.00 + }, + "tax_rate": 0.00 + }, + { + "tax_type": { + "account_name": "Umsatzsteuer 7%", + "account_number": "2302", + "tax_rate": 7.00 + }, + "tax_rate": 7.00 + } + ] + }, + { + "title": "Vorsteuer 19%", + "taxes": [ + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 19%", + "account_number": "1501", + "root_type": "Asset", + "tax_rate": 19.00 + }, + "tax_rate": 19.00 + }, + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 7%", + "account_number": "1502", + "root_type": "Asset", + "tax_rate": 7.00 + }, + "tax_rate": 0.00 + } + ] + }, + { + "title": "Vorsteuer 7%", + "taxes": [ + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 19%", + "account_number": "1501", + "root_type": "Asset", + "tax_rate": 19.00 + }, + "tax_rate": 0.00 + }, + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 7%", + "account_number": "1502", + "root_type": "Asset", + "tax_rate": 7.00 + }, + "tax_rate": 7.00 } ] } @@ -676,13 +755,67 @@ "*": { "sales_tax_templates": [ { - "title": "Umsatzsteuer 19%", + "title": "Umsatzsteuer", + "tax_category": "Umsatzsteuer", "taxes": [ { "account_head": { "account_name": "Umsatzsteuer 19%", "tax_rate": 19.00 - } + }, + "rate": 0.00 + }, + { + "account_head": { + "account_name": "Umsatzsteuer 7%", + "tax_rate": 7.00 + }, + "rate": 0.00 + } + ] + } + ], + "purchase_tax_templates": [ + { + "title": "Vorsteuer 19%", + "tax_category": "Vorsteuer", + "taxes": [ + { + "account_head": { + "account_name": "Abziehbare Vorsteuer 19%", + "tax_rate": 19.00, + "root_type": "Asset" + }, + "rate": 0.00 + }, + { + "account_head": { + "account_name": "Abziehbare Vorsteuer 7%", + "root_type": "Asset", + "tax_rate": 7.00 + }, + "rate": 0.00 + } + ] + } + ], + "item_tax_templates": [ + { + "title": "Umsatzsteuer 19%", + "taxes": [ + { + "tax_type": { + "account_name": "Umsatzsteuer 19%", + "tax_rate": 19.00 + }, + "tax_rate": 19.00 + }, + { + "tax_type": { + "account_name": "Umsatzsteuer 7%", + "tax_rate": 7.00 + }, + "tax_rate": 0.00 } ] }, @@ -690,36 +823,60 @@ "title": "Umsatzsteuer 7%", "taxes": [ { - "account_head": { + "tax_type": { + "account_name": "Umsatzsteuer 19%", + "tax_rate": 19.00 + }, + "tax_rate": 0.00 + }, + { + "tax_type": { "account_name": "Umsatzsteuer 7%", "tax_rate": 7.00 - } - } - ] - } - ], - "purchase_tax_templates": [ - { - "title": "Abziehbare Vorsteuer 19%", - "taxes": [ - { - "account_head": { - "account_name": "Abziehbare Vorsteuer 19%", - "tax_rate": 19.00, - "root_type": "Asset" - } + }, + "tax_rate": 7.00 } ] }, { - "title": "Abziehbare Vorsteuer 7%", + "title": "Vorsteuer 19%", "taxes": [ { - "account_head": { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 19%", + "root_type": "Asset", + "tax_rate": 19.00 + }, + "tax_rate": 19.00 + }, + { + "tax_type": { "account_name": "Abziehbare Vorsteuer 7%", "root_type": "Asset", "tax_rate": 7.00 - } + }, + "tax_rate": 0.00 + } + ] + }, + { + "title": "Vorsteuer 7%", + "taxes": [ + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 19%", + "root_type": "Asset", + "tax_rate": 19.00 + }, + "tax_rate": 0.00 + }, + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 7%", + "root_type": "Asset", + "tax_rate": 7.00 + }, + "tax_rate": 7.00 } ] } From 2e83cb77ca0147b8ab6fd5b0b999cfb5e6f213ea Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 27 May 2021 20:09:45 +0530 Subject: [PATCH 068/180] fix: Split GL Entry --- .../accounts/doctype/purchase_invoice/test_purchase_invoice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 8bb9d7dacf..1ec49d178f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1008,7 +1008,8 @@ class TestPurchaseInvoice(unittest.TestCase): ['_Test Account Cost for Goods Sold - _TC', 30000, 0], ['_Test Account Excise Duty - _TC', 0, 3000], ['Creditors - _TC', 0, 27000], - ['TDS Payable - _TC', 3000, 3000], + ['TDS Payable - _TC', 0, 3000], + ['TDS Payable - _TC', 3000, 0] ] gl_entries = frappe.db.sql("""select account, debit, credit From a150645b5712f4d7b595a1966c4f0b8600b917f2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 27 May 2021 20:10:50 +0530 Subject: [PATCH 069/180] fix: Remove GL Entry from print --- .../accounts/doctype/purchase_invoice/test_purchase_invoice.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 1ec49d178f..1c4350f8dc 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1017,9 +1017,6 @@ class TestPurchaseInvoice(unittest.TestCase): where voucher_type='Purchase Invoice' and voucher_no=%s order by account asc""", (purchase_invoice.name), as_dict=1) - for gle in gl_entries: - print(gle.account, gle.debit, gle.credit) - for i, gle in enumerate(gl_entries): self.assertEqual(expected_gle[i][0], gle.account) self.assertEqual(expected_gle[i][1], gle.debit) From 2f5885627db3f33d3e3f5138bf29ad57f6f46f09 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 1 Jun 2021 11:07:18 +0530 Subject: [PATCH 070/180] fix: Labeling and other fixes --- .../advance_taxes_and_charges.json | 80 ++++--------- .../doctype/payment_entry/payment_entry.js | 6 +- .../doctype/payment_entry/payment_entry.json | 5 +- .../purchase_taxes_and_charges.json | 105 +++++++----------- .../sales_taxes_and_charges.json | 38 ++++--- .../public/js/controllers/taxes_and_totals.js | 2 +- 6 files changed, 92 insertions(+), 144 deletions(-) diff --git a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json index aac7eb6bcb..c127601259 100644 --- a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json +++ b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json @@ -36,10 +36,8 @@ "label": "Type", "oldfieldname": "charge_type", "oldfieldtype": "Select", - "options": "\nActual\nOn Paid Amount\nIncluded In Paid Amount\nOn Previous Row Amount\nOn Previous Row Total", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "options": "\nActual\nOn Paid Amount\nOn Previous Row Amount\nOn Previous Row Total", + "reqd": 1 }, { "depends_on": "eval:[\"On Previous Row Amount\", \"On Previous Row Total\"].indexOf(doc.charge_type)!==-1", @@ -47,9 +45,7 @@ "fieldtype": "Data", "label": "Reference Row #", "oldfieldname": "row_id", - "oldfieldtype": "Data", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Data" }, { "columns": 2, @@ -61,15 +57,11 @@ "oldfieldtype": "Link", "options": "Account", "reqd": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "fieldname": "col_break_1", "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -80,16 +72,12 @@ "oldfieldtype": "Small Text", "print_width": "300px", "reqd": 1, - "show_days": 1, - "show_seconds": 1, "width": "300px" }, { "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", - "label": "Accounting Dimensions", - "show_days": 1, - "show_seconds": 1 + "label": "Accounting Dimensions" }, { "default": ":Company", @@ -98,21 +86,15 @@ "label": "Cost Center", "oldfieldname": "cost_center_other_charges", "oldfieldtype": "Link", - "options": "Cost Center", - "show_days": 1, - "show_seconds": 1 + "options": "Cost Center" }, { "fieldname": "dimension_col_break", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "section_break_8", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "columns": 2, @@ -121,15 +103,11 @@ "in_list_view": 1, "label": "Rate", "oldfieldname": "rate", - "oldfieldtype": "Currency", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Currency" }, { "fieldname": "section_break_9", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "columns": 2, @@ -137,9 +115,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", - "options": "currency", - "show_days": 1, - "show_seconds": 1 + "options": "currency" }, { "columns": 2, @@ -148,15 +124,11 @@ "in_list_view": 1, "label": "Total", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_13", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "base_tax_amount", @@ -165,9 +137,7 @@ "oldfieldname": "tax_amount", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_total", @@ -176,48 +146,38 @@ "oldfieldname": "total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "add_deduct_tax", "fieldtype": "Select", "label": "Add Or Deduct", "options": "Add\nDeduct", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "default": "0", "fieldname": "included_in_paid_amount", "fieldtype": "Check", - "label": "Included In Paid Amount", - "show_days": 1, - "show_seconds": 1 + "label": "Included In Paid Amount" }, { "fieldname": "allocated_amount", "fieldtype": "Currency", "label": "Allocated Amount", - "options": "currency", - "show_days": 1, - "show_seconds": 1 + "options": "currency" }, { "fieldname": "base_allocated_amount", "fieldtype": "Currency", "label": "Allocated Amount (Company Currency)", - "options": "Company:company:default_currency", - "show_days": 1, - "show_seconds": 1 + "options": "Company:company:default_currency" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-11-29 19:06:14.666460", + "modified": "2021-05-31 02:03:57.444647", "modified_by": "Administrator", "module": "Accounts", "name": "Advance Taxes and Charges", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index a4022ef40a..b1cf9e02e6 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -230,7 +230,7 @@ frappe.ui.form.on('Payment Entry', { var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: ""; frm.set_currency_labels(["base_paid_amount", "base_received_amount", "base_total_allocated_amount", - "difference_amount", "base_paid_amount_after_tax"], company_currency); + "difference_amount", "base_paid_amount_after_tax", "base_received_amount_after_tax"], company_currency); frm.set_currency_labels(["paid_amount"], frm.doc.paid_from_account_currency); frm.set_currency_labels(["received_amount"], frm.doc.paid_to_account_currency); @@ -238,11 +238,13 @@ frappe.ui.form.on('Payment Entry', { var party_account_currency = frm.doc.payment_type=="Receive" ? frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency; - frm.set_currency_labels(["total_allocated_amount", "unallocated_amount"], party_account_currency); + frm.set_currency_labels(["total_allocated_amount", "unallocated_amount", + "total_taxes_and_charges"], party_account_currency); var currency_field = (frm.doc.payment_type=="Receive") ? "paid_from_account_currency" : "paid_to_account_currency" frm.set_df_property("total_allocated_amount", "options", currency_field); frm.set_df_property("unallocated_amount", "options", currency_field); + frm.set_df_property("total_taxes_and_charges", "options", currency_field); frm.set_df_property("party_balance", "options", currency_field); frm.set_currency_labels(["total_amount", "outstanding_amount", "allocated_amount"], diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 1e2bd8194a..0b4178a547 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -335,6 +335,7 @@ "reqd": 1 }, { + "depends_on": "doc.received_amount", "fieldname": "base_received_amount", "fieldtype": "Currency", "label": "Received Amount (Company Currency)", @@ -683,6 +684,7 @@ "fieldname": "advance_tax_account", "fieldtype": "Link", "label": "Advance Tax Account", + "mandatory_depends_on": "doc.base_total_taxes_and_charges", "options": "Account" }, { @@ -692,6 +694,7 @@ "options": "paid_to_account_currency" }, { + "depends_on": "doc.received_amount", "fieldname": "base_received_amount_after_tax", "fieldtype": "Currency", "label": "Received Amount After Tax (Company Currency)", @@ -701,7 +704,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-05-20 02:04:56.766124", + "modified": "2021-05-31 01:54:18.378910", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json index 0db0b6823d..f6703315fa 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json @@ -12,6 +12,7 @@ "charge_type", "row_id", "included_in_print_rate", + "included_in_paid_amount", "col_break1", "account_head", "description", @@ -21,6 +22,7 @@ "cost_center", "dimension_col_break", "section_break_9", + "currency", "tax_amount", "tax_amount_after_discount_amount", "total", @@ -39,9 +41,7 @@ "oldfieldname": "category", "oldfieldtype": "Select", "options": "Valuation and Total\nValuation\nTotal", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "default": "Add", @@ -51,9 +51,7 @@ "oldfieldname": "add_deduct_tax", "oldfieldtype": "Select", "options": "Add\nDeduct", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "columns": 2, @@ -65,9 +63,7 @@ "oldfieldname": "charge_type", "oldfieldtype": "Select", "options": "\nActual\nOn Net Total\nOn Previous Row Amount\nOn Previous Row Total\nOn Item Quantity", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "depends_on": "eval:[\"On Previous Row Amount\", \"On Previous Row Total\"].indexOf(doc.charge_type)!==-1", @@ -75,9 +71,7 @@ "fieldtype": "Data", "label": "Reference Row #", "oldfieldname": "row_id", - "oldfieldtype": "Data", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Data" }, { "default": "0", @@ -85,15 +79,11 @@ "fieldname": "included_in_print_rate", "fieldtype": "Check", "label": "Is this Tax included in Basic Rate?", - "report_hide": 1, - "show_days": 1, - "show_seconds": 1 + "report_hide": 1 }, { "fieldname": "col_break1", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "columns": 2, @@ -104,9 +94,7 @@ "oldfieldname": "account_head", "oldfieldtype": "Link", "options": "Account", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "default": ":Company", @@ -115,9 +103,7 @@ "label": "Cost Center", "oldfieldname": "cost_center", "oldfieldtype": "Link", - "options": "Cost Center", - "show_days": 1, - "show_seconds": 1 + "options": "Cost Center" }, { "fieldname": "description", @@ -127,15 +113,11 @@ "oldfieldtype": "Small Text", "print_width": "300px", "reqd": 1, - "show_days": 1, - "show_seconds": 1, "width": "300px" }, { "fieldname": "section_break_10", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "columns": 2, @@ -144,15 +126,11 @@ "in_list_view": 1, "label": "Rate", "oldfieldname": "rate", - "oldfieldtype": "Currency", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Currency" }, { "fieldname": "section_break_9", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "columns": 2, @@ -162,9 +140,7 @@ "label": "Amount", "oldfieldname": "tax_amount", "oldfieldtype": "Currency", - "options": "currency", - "show_days": 1, - "show_seconds": 1 + "options": "currency" }, { "fieldname": "tax_amount_after_discount_amount", @@ -172,9 +148,7 @@ "label": "Tax Amount After Discount Amount", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "columns": 2, @@ -185,15 +159,11 @@ "oldfieldname": "total", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_14", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "base_tax_amount", @@ -201,9 +171,7 @@ "label": "Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_total", @@ -211,9 +179,7 @@ "hidden": 1, "label": "Total (Company Currency)", "options": "Company:company:default_currency", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "base_tax_amount_after_discount_amount", @@ -221,9 +187,7 @@ "label": "Tax Amount After Discount Amount", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "item_wise_tax_detail", @@ -233,28 +197,37 @@ "oldfieldname": "item_wise_tax_detail", "oldfieldtype": "Small Text", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", - "label": "Accounting Dimensions", - "show_days": 1, - "show_seconds": 1 + "label": "Accounting Dimensions" }, { "fieldname": "dimension_col_break", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" + }, + { + "fetch_from": "account_head.account_currency", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Account Currency", + "options": "Currency", + "read_only": 1 + }, + { + "default": "0", + "description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry", + "fieldname": "included_in_paid_amount", + "fieldtype": "Check", + "label": "Included In Paid Amount" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-11-29 19:11:58.826078", + "modified": "2021-05-31 03:41:38.298937", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Taxes and Charges", diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json index 3c8cb6b851..36fd634b50 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json @@ -1,8 +1,10 @@ { + "actions": [], "creation": "2013-04-24 11:39:32", "doctype": "DocType", "document_type": "Setup", "editable_grid": 1, + "engine": "InnoDB", "field_order": [ "charge_type", "row_id", @@ -10,12 +12,14 @@ "col_break_1", "description", "included_in_print_rate", + "included_in_paid_amount", "accounting_dimensions_section", "cost_center", "dimension_col_break", "section_break_8", "rate", "section_break_9", + "currency", "tax_amount", "total", "tax_amount_after_discount_amount", @@ -23,8 +27,7 @@ "base_tax_amount", "base_total", "base_tax_amount_after_discount_amount", - "item_wise_tax_detail", - "parenttype" + "item_wise_tax_detail" ], "fields": [ { @@ -173,17 +176,6 @@ "oldfieldtype": "Small Text", "read_only": 1 }, - { - "fieldname": "parenttype", - "fieldtype": "Data", - "hidden": 1, - "in_filter": 1, - "label": "Parenttype", - "oldfieldname": "parenttype", - "oldfieldtype": "Data", - "print_hide": 1, - "search_index": 1 - }, { "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", @@ -192,15 +184,33 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fetch_from": "account_head.account_currency", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Account Currency", + "options": "Currency", + "read_only": 1 + }, + { + "default": "0", + "description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry", + "fieldname": "included_in_paid_amount", + "fieldtype": "Check", + "label": "Included In Paid Amount" } ], "idx": 1, + "index_web_pages_for_search": 1, "istable": 1, - "modified": "2019-05-25 22:59:38.740883", + "links": [], + "modified": "2021-05-31 05:40:32.856780", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges", "owner": "Administrator", "permissions": [], + "sort_field": "modified", "sort_order": "ASC" } \ No newline at end of file diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 2e133bed2e..3097f72201 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -464,7 +464,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ }, calculate_totals: function() { - // Changing sequence can cause rounding_adjustmentng issue and on-screen discrepency + // Changing sequence can because of rounding adjustment issue and on-screen discrepancy var me = this; var tax_count = this.frm.doc["taxes"] ? this.frm.doc["taxes"].length : 0; this.frm.doc.grand_total = flt(tax_count From c6dcc9d94a19fef68ebaf792063beb0fd19c8c3a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 1 Jun 2021 13:13:04 +0530 Subject: [PATCH 071/180] fix(India): Taxable value for invoices with additional discount --- erpnext/regional/india/e_invoice/utils.py | 30 ++++++----------------- erpnext/regional/india/utils.py | 8 ++---- 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index 843fb012b9..95b4e16afd 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -38,7 +38,7 @@ def validate_eligibility(doc): einvoicing_eligible_from = frappe.db.get_single_value('E Invoice Settings', 'applicable_from') or '2021-04-01' if getdate(doc.get('posting_date')) < getdate(einvoicing_eligible_from): return False - + invalid_company = not frappe.db.get_value('E Invoice User', { 'company': doc.get('company') }) 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') @@ -135,7 +135,7 @@ def validate_address_fields(address, is_shipping_address): def get_party_details(address_name, is_shipping_address=False): addr = frappe.get_doc('Address', address_name) - + validate_address_fields(addr, is_shipping_address) if addr.gst_state_number == 97: @@ -188,11 +188,6 @@ def get_item_list(invoice): item.qty = abs(item.qty) - if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount: - item.discount_amount = abs(item.base_amount - item.base_net_amount) - else: - item.discount_amount = 0 - item.unit_rate = abs((abs(item.taxable_value) - item.discount_amount)/ item.qty) item.gross_amount = abs(item.taxable_value) + item.discount_amount item.taxable_value = abs(item.taxable_value) @@ -254,18 +249,8 @@ def update_item_taxes(invoice, item): def get_invoice_value_details(invoice): invoice_value_details = frappe._dict(dict()) - - if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount: - # Discount already applied on net total which means on items - invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')])) - invoice_value_details.invoice_discount_amt = 0 - elif invoice.apply_discount_on == 'Grand Total' and invoice.discount_amount: - invoice_value_details.invoice_discount_amt = invoice.base_discount_amount - invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')])) - else: - invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')])) - # since tax already considers discount amount - invoice_value_details.invoice_discount_amt = 0 + invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')])) + invoice_value_details.invoice_discount_amt = 0 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) @@ -287,8 +272,7 @@ def update_invoice_taxes(invoice, invoice_value_details): considered_rows = [] for t in invoice.taxes: - tax_amount = t.base_tax_amount if (invoice.apply_discount_on == 'Grand Total' and invoice.discount_amount) \ - else t.base_tax_amount_after_discount_amount + tax_amount = t.base_tax_amount_after_discount_amount if t.account_head in gst_accounts_list: if t.account_head in gst_accounts.cess_account: # using after discount amt since item also uses after discount amt for cess calc @@ -995,7 +979,7 @@ class GSPConnector(): self.invoice.failure_description = self.get_failure_message(errors) if errors else "" self.update_invoice() frappe.db.commit() - + def get_failure_message(self, errors): if isinstance(errors, list): errors = ', '.join(errors) @@ -1052,7 +1036,7 @@ def generate_einvoices(docnames): _('{} e-invoices generated successfully').format(success), title=_('Bulk E-Invoice Generation Complete') ) - + else: enqueue_bulk_action(schedule_bulk_generate_irn, docnames=docnames) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index fc227defbf..ea61502099 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -817,12 +817,8 @@ def update_taxable_values(doc, method): considered_rows.append(prev_row_id) for item in doc.get('items'): - if doc.apply_discount_on == 'Grand Total' and doc.discount_amount: - proportionate_value = item.base_amount if doc.base_total else item.qty - total_value = doc.base_total if doc.base_total else doc.total_qty - else: - proportionate_value = item.base_net_amount if doc.base_net_total else item.qty - total_value = doc.base_net_total if doc.base_net_total else doc.total_qty + proportionate_value = item.base_net_amount if doc.base_net_total else item.qty + total_value = doc.base_net_total if doc.base_net_total else doc.total_qty applicable_charges = flt(flt(proportionate_value * (flt(additional_taxes) / flt(total_value)), item.precision('taxable_value'))) From 721b4130db37e0219843b0fbca9acd1a36ee816b Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 2 Jun 2021 14:13:09 +0530 Subject: [PATCH 072/180] fix: not able to select the item code in work order --- erpnext/controllers/queries.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 46301b7587..638503edfa 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -204,7 +204,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals if "description" in searchfields: searchfields.remove("description") - + columns = '' extra_searchfields = [field for field in searchfields if not field in ["name", "item_group", "description"]] @@ -216,9 +216,10 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals if not field in searchfields] searchfields = " or ".join([field + " like %(txt)s" for field in searchfields]) - if filters.get('supplier'): - item_group_list = frappe.get_all('Supplier Item Group', filters = {'supplier': filters.get('supplier')}, fields = ['item_group']) - + if filters and isinstance(filters, dict) and filters.get('supplier'): + item_group_list = frappe.get_all('Supplier Item Group', + filters = {'supplier': filters.get('supplier')}, fields = ['item_group']) + item_groups = [] for i in item_group_list: item_groups.append(i.item_group) @@ -227,7 +228,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals if item_groups: filters['item_group'] = ['in', item_groups] - + description_cond = '' if frappe.db.count('Item', cache=True) < 50000: # scan description only if items are less than 50000 From a06ec03efcaedeeffaf9b8f9818bfb866f44e2d1 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Wed, 2 Jun 2021 14:55:31 +0530 Subject: [PATCH 073/180] test: add test for new QI function --- erpnext/controllers/stock_controller.py | 5 +- erpnext/public/js/controllers/transaction.js | 2 +- .../test_quality_inspection.py | 152 ++++++++++++------ 3 files changed, 103 insertions(+), 56 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index abc966a477..0da723d56e 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -504,9 +504,10 @@ class StockController(AccountsController): @frappe.whitelist() def make_quality_inspections(doctype, docname, items): - items = json.loads(items).get('items') - inspections = [] + if isinstance(items, str): + items = json.loads(items) + inspections = [] for item in items: if flt(item.get("sample_size")) > flt(item.get("qty")): frappe.throw(_("{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity})").format( diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 95562baa8e..982b1fe1eb 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -2035,7 +2035,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ args: { doctype: me.frm.doc.doctype, docname: me.frm.doc.name, - items: data + items: data.items }, freeze: true, callback: function (r) { diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py index 56b046a92e..7f3d701034 100644 --- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py @@ -1,29 +1,45 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors # See license.txt -from __future__ import unicode_literals -import frappe import unittest + +import frappe from frappe.utils import nowdate -from erpnext.stock.doctype.item.test_item import create_item + +from erpnext.controllers.stock_controller import ( + QualityInspectionNotSubmittedError, + QualityInspectionRejectedError, + QualityInspectionRequiredError, + make_quality_inspections, +) from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note +from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry -from erpnext.controllers.stock_controller import QualityInspectionRejectedError, QualityInspectionRequiredError, QualityInspectionNotSubmittedError # test_records = frappe.get_test_records('Quality Inspection') + class TestQualityInspection(unittest.TestCase): def setUp(self): create_item("_Test Item with QA") - frappe.db.set_value("Item", "_Test Item with QA", "inspection_required_before_delivery", 1) + frappe.db.set_value( + "Item", "_Test Item with QA", "inspection_required_before_delivery", 1 + ) def test_qa_for_delivery(self): - make_stock_entry(item_code="_Test Item with QA", target="_Test Warehouse - _TC", qty=1, basic_rate=100) + make_stock_entry( + item_code="_Test Item with QA", + target="_Test Warehouse - _TC", + qty=1, + basic_rate=100 + ) dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True) self.assertRaises(QualityInspectionRequiredError, dn.submit) - qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected") + qa = create_quality_inspection( + reference_type="Delivery Note", reference_name=dn.name, status="Rejected" + ) dn.reload() self.assertRaises(QualityInspectionRejectedError, dn.submit) @@ -38,7 +54,9 @@ class TestQualityInspection(unittest.TestCase): def test_qa_not_submit(self): dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True) - qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, do_not_submit=True) + qa = create_quality_inspection( + reference_type="Delivery Note", reference_name=dn.name, do_not_submit=True + ) dn.items[0].quality_inspection = qa.name self.assertRaises(QualityInspectionNotSubmittedError, dn.submit) @@ -48,21 +66,28 @@ class TestQualityInspection(unittest.TestCase): 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 - "numeric": 0, - "value": "Yes", - "reading_value": "Yes" - }] + 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 + "numeric": 0, + "value": "Yes", + "reading_value": "Yes" + } + ] + + qa = create_quality_inspection( + reference_type="Delivery Note", + reference_name=dn.name, + readings=readings, + do_not_save=True + ) - 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 @@ -74,36 +99,43 @@ class TestQualityInspection(unittest.TestCase): 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", # numeric reading - "formula_based_criteria": 1, - "acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50", - "reading_1": "0.4" - }, - { - "specification": "Calcium Content", # numeric reading - "formula_based_criteria": 1, - "acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50", - "reading_1": "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, - "numeric": 0, - "acceptance_formula": "reading_value in ('Grade A', 'Grade B', 'Grade C')", - "reading_value": "Grade B" - }] + readings = [ + { + "specification": "Iron Content", # numeric reading + "formula_based_criteria": 1, + "acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50", + "reading_1": "0.4" + }, + { + "specification": "Calcium Content", # numeric reading + "formula_based_criteria": 1, + "acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50", + "reading_1": "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, + "numeric": 0, + "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, + readings=readings, + do_not_save=True + ) - 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 @@ -115,6 +147,19 @@ class TestQualityInspection(unittest.TestCase): qa.delete() dn.delete() + def test_make_quality_inspections_from_linked_document(self): + dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True) + for item in dn.items: + item.sample_size = item.qty + quality_inspections = make_quality_inspections(dn.doctype, dn.name, dn.items) + self.assertEqual(len(dn.items), len(quality_inspections)) + + # cleanup + for qi in quality_inspections: + frappe.delete_doc("Quality Inspection", qi) + dn.delete() + + def create_quality_inspection(**args): args = frappe._dict(args) qa = frappe.new_doc("Quality Inspection") @@ -134,7 +179,7 @@ def create_quality_inspection(**args): readings = args.readings if args.status == "Rejected": - readings["reading_1"] = "12" # status is auto set in child on save + readings["reading_1"] = "12" # status is auto set in child on save if isinstance(readings, list): for entry in readings: @@ -150,10 +195,11 @@ def create_quality_inspection(**args): return qa + def create_quality_inspection_parameter(parameter): if not frappe.db.exists("Quality Inspection Parameter", parameter): frappe.get_doc({ "doctype": "Quality Inspection Parameter", "parameter": parameter, "description": parameter - }).insert() \ No newline at end of file + }).insert() From f259bf48aaa40630b0a7b08b6d323a14ffc8aa03 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 2 Jun 2021 13:20:03 +0200 Subject: [PATCH 074/180] refactor: make tax category --- .../setup_wizard/data/country_wise_tax.json | 4 +++ .../setup_wizard/operations/taxes_setup.py | 33 +++++++++++-------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json index 2e2a0ca726..5ed8ab92bf 100644 --- a/erpnext/setup/setup_wizard/data/country_wise_tax.json +++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json @@ -481,6 +481,10 @@ }, "Germany": { + "tax_categories": [ + "Umsatzsteuer", + "Vorsteuer" + ], "chart_of_accounts": { "SKR04 mit Kontonummern": { "sales_tax_templates": [ diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py index 4a3de957b9..f4fe18e116 100644 --- a/erpnext/setup/setup_wizard/operations/taxes_setup.py +++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py @@ -26,7 +26,7 @@ def setup_taxes_and_charges(company_name: str, country: str): if 'chart_of_accounts' not in country_wise_tax: country_wise_tax = simple_to_detailed(country_wise_tax) - from_detailed_data(company_name, country_wise_tax.get('chart_of_accounts')) + from_detailed_data(company_name, country_wise_tax) def simple_to_detailed(templates): @@ -77,10 +77,16 @@ def simple_to_detailed(templates): def from_detailed_data(company_name, data): """Create Taxes and Charges Templates from detailed data.""" coa_name = frappe.db.get_value('Company', company_name, 'chart_of_accounts') - tax_templates = data.get(coa_name) or data.get('*') - sales_tax_templates = tax_templates.get('sales_tax_templates') or tax_templates.get('*') - purchase_tax_templates = tax_templates.get('purchase_tax_templates') or tax_templates.get('*') - item_tax_templates = tax_templates.get('item_tax_templates') or tax_templates.get('*') + coa_data = data.get('chart_of_accounts', {}) + tax_templates = coa_data.get(coa_name) or coa_data.get('*', {}) + tax_categories = data.get('tax_categories') + sales_tax_templates = tax_templates.get('sales_tax_templates') or tax_templates.get('*', {}) + purchase_tax_templates = tax_templates.get('purchase_tax_templates') or tax_templates.get('*', {}) + item_tax_templates = tax_templates.get('item_tax_templates') or tax_templates.get('*', {}) + + if tax_categories: + for tax_category in tax_categories: + make_tax_catgory(tax_category) if sales_tax_templates: for template in sales_tax_templates: @@ -102,9 +108,6 @@ def make_taxes_and_charges_template(company_name, doctype, template): if frappe.db.exists(doctype, {'title': template.get('title'), 'company': company_name}): return - if template.get('tax_category'): - ensure_tax_category_exists(template.get('tax_category')) - for tax_row in template.get('taxes'): account_data = tax_row.get('account_head') tax_row_defaults = { @@ -241,8 +244,12 @@ def get_or_create_tax_group(company_name, root_type): return tax_group_name -def ensure_tax_category_exists(name): - if not frappe.db.exists('Tax Category', name): - doc = frappe.new_doc('Tax Category') - doc.title = name - doc.save() +def make_tax_catgory(tax_category): + doctype = 'Tax Category' + if isinstance(tax_category, str): + tax_category = {'title': tax_category} + + tax_category['doctype'] = doctype + if not frappe.db.exists(doctype, tax_category['title']): + doc = frappe.get_doc(tax_category) + doc.insert(ignore_permissions=True) From b6f27a4caef1fd479762579fd6f612fdc78fa69d Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 2 Jun 2021 13:20:33 +0200 Subject: [PATCH 075/180] feat: set is_default for german tax templates --- erpnext/setup/setup_wizard/data/country_wise_tax.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json index 5ed8ab92bf..f4728ef398 100644 --- a/erpnext/setup/setup_wizard/data/country_wise_tax.json +++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json @@ -491,6 +491,7 @@ { "title": "Umsatzsteuer", "tax_category": "Umsatzsteuer", + "is_default": 1, "taxes": [ { "account_head": { @@ -515,6 +516,7 @@ { "title": "Vorsteuer", "tax_category": "Vorsteuer", + "is_default": 1, "taxes": [ { "account_head": { @@ -567,6 +569,7 @@ { "title": "Umsatzsteuer", "tax_category": "Umsatzsteuer", + "is_default": 1, "taxes": [ { "account_head": { @@ -591,6 +594,7 @@ { "title": "Vorsteuer", "tax_category": "Vorsteuer", + "is_default": 1, "taxes": [ { "account_head": { @@ -619,6 +623,7 @@ { "title": "Umsatzsteuer", "tax_category": "Umsatzsteuer", + "is_default": 1, "taxes": [ { "account_head": { @@ -643,6 +648,7 @@ { "title": "Vorsteuer", "tax_category": "Vorsteuer", + "is_default": 1, "taxes": [ { "account_head": { @@ -761,6 +767,7 @@ { "title": "Umsatzsteuer", "tax_category": "Umsatzsteuer", + "is_default": 1, "taxes": [ { "account_head": { @@ -783,6 +790,7 @@ { "title": "Vorsteuer 19%", "tax_category": "Vorsteuer", + "is_default": 1, "taxes": [ { "account_head": { From 3c748efae3cfc78a88447ac9edeccfea860f1358 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 2 Jun 2021 13:27:15 +0200 Subject: [PATCH 076/180] feat: add Item Tax Templates for german COAs "SKR03" and "SKR04" --- .../setup_wizard/data/country_wise_tax.json | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json index f4728ef398..8305cae73e 100644 --- a/erpnext/setup/setup_wizard/data/country_wise_tax.json +++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json @@ -562,6 +562,96 @@ } ] } + ], + "item_tax_templates": [ + { + "title": "Umsatzsteuer 19%", + "taxes": [ + { + "tax_type": { + "account_name": "Umsatzsteuer 19%", + "account_number": "3806", + "tax_rate": 19.00 + }, + "tax_rate": 19.00 + }, + { + "tax_type": { + "account_name": "Umsatzsteuer 7%", + "account_number": "3801", + "tax_rate": 7.00 + }, + "tax_rate": 0.00 + } + ] + }, + { + "title": "Umsatzsteuer 7%", + "taxes": [ + { + "tax_type": { + "account_name": "Umsatzsteuer 19%", + "account_number": "3806", + "tax_rate": 19.00 + }, + "tax_rate": 0.00 + }, + { + "tax_type": { + "account_name": "Umsatzsteuer 7%", + "account_number": "3801", + "tax_rate": 7.00 + }, + "tax_rate": 7.00 + } + ] + }, + { + "title": "Vorsteuer 19%", + "taxes": [ + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 19%", + "account_number": "1406", + "root_type": "Asset", + "tax_rate": 19.00 + }, + "tax_rate": 19.00 + }, + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 7%", + "account_number": "1401", + "root_type": "Asset", + "tax_rate": 7.00 + }, + "tax_rate": 0.00 + } + ] + }, + { + "title": "Vorsteuer 7%", + "taxes": [ + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 19%", + "account_number": "1406", + "root_type": "Asset", + "tax_rate": 19.00 + }, + "tax_rate": 0.00 + }, + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 7%", + "account_number": "1401", + "root_type": "Asset", + "tax_rate": 7.00 + }, + "tax_rate": 7.00 + } + ] + } ] }, "SKR03 mit Kontonummern": { @@ -616,6 +706,96 @@ } ] } + ], + "item_tax_templates": [ + { + "title": "Umsatzsteuer 19%", + "taxes": [ + { + "tax_type": { + "account_name": "Umsatzsteuer 19%", + "account_number": "1776", + "tax_rate": 19.00 + }, + "tax_rate": 19.00 + }, + { + "tax_type": { + "account_name": "Umsatzsteuer 7%", + "account_number": "1771", + "tax_rate": 7.00 + }, + "tax_rate": 0.00 + } + ] + }, + { + "title": "Umsatzsteuer 7%", + "taxes": [ + { + "tax_type": { + "account_name": "Umsatzsteuer 19%", + "account_number": "1776", + "tax_rate": 19.00 + }, + "tax_rate": 0.00 + }, + { + "tax_type": { + "account_name": "Umsatzsteuer 7%", + "account_number": "1771", + "tax_rate": 7.00 + }, + "tax_rate": 7.00 + } + ] + }, + { + "title": "Vorsteuer 19%", + "taxes": [ + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 19%", + "account_number": "1576", + "root_type": "Asset", + "tax_rate": 19.00 + }, + "tax_rate": 19.00 + }, + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 7%", + "account_number": "1571", + "root_type": "Asset", + "tax_rate": 7.00 + }, + "tax_rate": 0.00 + } + ] + }, + { + "title": "Vorsteuer 7%", + "taxes": [ + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 19%", + "account_number": "1576", + "root_type": "Asset", + "tax_rate": 19.00 + }, + "tax_rate": 0.00 + }, + { + "tax_type": { + "account_name": "Abziehbare Vorsteuer 7%", + "account_number": "1571", + "root_type": "Asset", + "tax_rate": 7.00 + }, + "tax_rate": 7.00 + } + ] + } ] }, "Standard with Numbers": { From f6627550d11ea95170ce5f208a257bb31218585f Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Wed, 2 Jun 2021 15:17:00 +0200 Subject: [PATCH 077/180] fix: remove wrong tax_category --- erpnext/setup/setup_wizard/data/country_wise_tax.json | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json index 8305cae73e..ec9a6d6b70 100644 --- a/erpnext/setup/setup_wizard/data/country_wise_tax.json +++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json @@ -540,7 +540,6 @@ }, { "title": "Innergemeinschaftlicher Erwerb 19% Umsatzsteuer und 19% Vorsteuer", - "tax_category": "Innergemeinschaftlicher Erwerb 19%", "taxes": [ { "account_head": { From 311b378328ec3d8ea774749f69188210eef237cd Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 3 Jun 2021 12:58:53 +0530 Subject: [PATCH 078/180] fix(pos): searching items with barcode or serial no --- .../page/point_of_sale/point_of_sale.py | 88 ++++++++++--------- .../page/point_of_sale/pos_item_selector.js | 6 +- 2 files changed, 49 insertions(+), 45 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index cb811df8e8..7742f24385 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -8,38 +8,52 @@ from frappe.utils import cint from erpnext.accounts.doctype.pos_profile.pos_profile import get_item_groups from erpnext.accounts.doctype.pos_invoice.pos_invoice import get_stock_availability -from six import string_types +def search_by_term(search_term, warehouse, price_list): + result = search_for_serial_or_batch_or_barcode_number(search_term) + + item_code = result.get("item_code") or search_term + serial_no = result.get("serial_no") or "" + batch_no = result.get("batch_no") or "" + barcode = result.get("barcode") or "" + + if result: + item_info = frappe.db.get_value("Item", item_code, + ["name as item_code", "item_name", "description", "stock_uom", "image as item_image", "is_stock_item"], + as_dict=1) + + item_stock_qty = get_stock_availability(item_code, warehouse) + price_list_rate, currency = frappe.db.get_value('Item Price', { + 'price_list': price_list, + 'item_code': item_code + }, ["price_list_rate", "currency"]) + + item_info.update({ + 'serial_no': serial_no, + 'batch_no': batch_no, + 'barcode': barcode, + 'price_list_rate': price_list_rate, + 'currency': currency, + 'actual_qty': item_stock_qty + }) + + return {'items': [item_info]} @frappe.whitelist() -def get_items(start, page_length, price_list, item_group, pos_profile, search_value=""): - data = dict() +def get_items(start, page_length, price_list, item_group, pos_profile, search_term=""): + warehouse, hide_unavailable_items = frappe.db.get_value( + 'POS Profile', pos_profile, ['warehouse', 'hide_unavailable_items']) + result = [] - warehouse, hide_unavailable_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'hide_unavailable_items']) + if search_term: + result = search_by_term(search_term, warehouse, price_list) + if result: + return result if not frappe.db.exists('Item Group', item_group): item_group = get_root_of('Item Group') - if search_value: - data = search_serial_or_batch_or_barcode_number(search_value) - - item_code = data.get("item_code") if data.get("item_code") else search_value - serial_no = data.get("serial_no") if data.get("serial_no") else "" - batch_no = data.get("batch_no") if data.get("batch_no") else "" - barcode = data.get("barcode") if data.get("barcode") else "" - - if data: - item_info = frappe.db.get_value( - "Item", data.get("item_code"), - ["name as item_code", "item_name", "description", "stock_uom", "image as item_image", "is_stock_item"] - , as_dict=1) - item_info.setdefault('serial_no', serial_no) - item_info.setdefault('batch_no', batch_no) - item_info.setdefault('barcode', barcode) - - return { 'items': [item_info] } - - condition = get_conditions(item_code, serial_no, batch_no, barcode) + condition = get_conditions(search_term) condition += get_item_group_condition(pos_profile) lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt']) @@ -106,14 +120,10 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_va }) result.append(row) - res = { - 'items': result - } - - return res + return {'items': result} @frappe.whitelist() -def search_serial_or_batch_or_barcode_number(search_value): +def search_for_serial_or_batch_or_barcode_number(search_value): # search barcode no barcode_data = frappe.db.get_value('Item Barcode', {'barcode': search_value}, ['barcode', 'parent as item_code'], as_dict=True) if barcode_data: @@ -139,27 +149,21 @@ def filter_service_items(items): return items -def get_conditions(item_code, serial_no, batch_no, barcode): - if serial_no or batch_no or barcode: - return "item.name = {0}".format(frappe.db.escape(item_code)) - - return make_condition(item_code) - -def make_condition(item_code): +def get_conditions(search_term): condition = "(" - condition += """item.name like {item_code} - or item.item_name like {item_code}""".format(item_code = frappe.db.escape('%' + item_code + '%')) - condition += add_search_fields_condition(item_code) + condition += """item.name like {search_term} + or item.item_name like {search_term}""".format(search_term=frappe.db.escape('%' + search_term + '%')) + condition += add_search_fields_condition(search_term) condition += ")" return condition -def add_search_fields_condition(item_code): +def add_search_fields_condition(search_term): condition = '' search_fields = frappe.get_all('POS Search Fields', fields = ['fieldname']) if search_fields: for field in search_fields: - condition += " or item.{0} like {1}".format(field['fieldname'], frappe.db.escape('%' + item_code + '%')) + condition += " or item.`{0}` like {1}".format(field['fieldname'], frappe.db.escape('%' + search_term + '%')) return condition def get_item_group_condition(pos_profile): diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 5b48725d9c..3e3377e5dc 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -51,7 +51,7 @@ erpnext.PointOfSale.ItemSelector = class { }); } - get_items({start = 0, page_length = 40, search_value=''}) { + get_items({start = 0, page_length = 40, search_term=''}) { const doc = this.events.get_frm().doc; const price_list = (doc && doc.selling_price_list) || this.price_list; let { item_group, pos_profile } = this; @@ -61,7 +61,7 @@ erpnext.PointOfSale.ItemSelector = class { return frappe.call({ method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items", freeze: true, - args: { start, page_length, price_list, item_group, search_value, pos_profile }, + args: { start, page_length, price_list, item_group, search_term, pos_profile }, }); } @@ -302,7 +302,7 @@ erpnext.PointOfSale.ItemSelector = class { } } - this.get_items({ search_value: search_term }) + this.get_items({ search_term }) .then(({ message }) => { // eslint-disable-next-line no-unused-vars const { items, serial_no, batch_no, barcode } = message; From 81b9a5ee4776613ad8720e7004267a82037715f4 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 3 Jun 2021 12:59:09 +0530 Subject: [PATCH 079/180] fix(pos): cannot cancel consolidated sales invoice --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 1808005f62..f813425e6b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -17,7 +17,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte var me = this; this._super(); - this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet']; + this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log', 'POS Closing Entry']; if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) { // show debit_to in print format this.frm.set_df_property("debit_to", "print_hide", 0); From 10558344b0fa25b12f71fda9ca21bb8c5170cb83 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 27 May 2021 17:05:36 +0530 Subject: [PATCH 080/180] fix: timeout error in the repost item valuation --- erpnext/hooks.py | 4 +++- .../repost_item_valuation/repost_item_valuation.py | 13 ++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 55169dffba..8ad77a1524 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -332,7 +332,9 @@ 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", + "erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders" + ], + "hourly_long": [ "erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries" ], "daily": [ 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 5b626ea345..55f2ebb224 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -37,6 +37,9 @@ class RepostItemValuation(Document): self.db_set('status', status) def on_submit(self): + if not frappe.flags.in_test: + return + frappe.enqueue(repost, timeout=1800, queue='long', job_name='repost_sle', now=frappe.flags.in_test, doc=self) @@ -115,12 +118,6 @@ def notify_error_to_stock_managers(doc, traceback): frappe.sendmail(recipients=recipients, subject=subject, message=message) def repost_entries(): - job_log = frappe.get_all('Scheduled Job Log', fields = ['status', 'creation'], - filters = {'scheduled_job_type': 'repost_item_valuation.repost_entries'}, order_by='creation desc', limit=1) - - if job_log and job_log[0]['status'] == 'Start' and time_diff_in_hours(now(), job_log[0]['creation']) < 2: - return - riv_entries = get_repost_item_valuation_entries() for row in riv_entries: @@ -135,9 +132,7 @@ def repost_entries(): check_if_stock_and_account_balance_synced(today(), d.name) def get_repost_item_valuation_entries(): - date = add_to_date(now(), hours=-3) - return frappe.db.sql(""" SELECT name from `tabRepost Item Valuation` WHERE status != 'Completed' and creation <= %s and docstatus = 1 ORDER BY timestamp(posting_date, posting_time) asc, creation asc - """, date, as_dict=1) + """, now(), as_dict=1) From c59371ab0c500d9d863db334931d527e8face6eb Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 3 Jun 2021 20:02:58 +0530 Subject: [PATCH 081/180] fix: filter type for item query (#25942) --- erpnext/controllers/queries.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 638503edfa..81ac234e70 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe import erpnext +import json from frappe.desk.reportview import get_match_cond, get_filters_cond from frappe.utils import nowdate, getdate from collections import defaultdict @@ -198,6 +199,9 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters): def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): conditions = [] + if isinstance(filters, str): + filters = json.loads(filters) + #Get searchfields from meta and use in Item Link field query meta = frappe.get_meta("Item", cached=True) searchfields = meta.get_search_fields() From 48036b4bab25fcecf2728decc78ae2c598f54c0e Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 4 Jun 2021 09:54:34 +0530 Subject: [PATCH 082/180] fix: invoices can alter profit and loss of a closed year --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 948c51364e..11465b711e 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -121,8 +121,7 @@ class GLEntry(Document): def check_pl_account(self): if self.is_opening=='Yes' and \ - frappe.db.get_value("Account", self.account, "report_type")=="Profit and Loss" and \ - self.voucher_type not in ['Purchase Invoice', 'Sales Invoice']: + frappe.db.get_value("Account", self.account, "report_type")=="Profit and Loss": frappe.throw(_("{0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry") .format(self.voucher_type, self.voucher_no, self.account)) From c6e016e5452950f393348710f6872febc9f9fb04 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 4 Jun 2021 10:08:22 +0530 Subject: [PATCH 083/180] fix: wrong round off gl entry posted in case of purchase invoice (#25775) --- erpnext/accounts/general_ledger.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index f1717c50d8..d4b249429b 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -185,10 +185,10 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision): for d in gl_map: if d.account == round_off_account: round_off_gle = d - if d.debit_in_account_currency: - debit_credit_diff -= flt(d.debit_in_account_currency) + if d.debit: + debit_credit_diff -= flt(d.debit) else: - debit_credit_diff += flt(d.credit_in_account_currency) + debit_credit_diff += flt(d.credit) round_off_account_exists = True if round_off_account_exists and abs(debit_credit_diff) <= (1.0 / (10**precision)): From af4794b2d13b9499bff8333e1cb0b479e8405032 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 4 Jun 2021 10:53:44 +0530 Subject: [PATCH 084/180] fix: custom conversion factor field not mapped from job card to stock entry --- erpnext/manufacturing/doctype/job_card/job_card.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index fb26062566..d764db33f8 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -433,7 +433,8 @@ def make_material_request(source_name, target_doc=None): def make_stock_entry(source_name, target_doc=None): def update_item(obj, target, source_parent): target.t_warehouse = source_parent.wip_warehouse - target.conversion_factor = 1 + if not target.conversion_factor: + target.conversion_factor = 1 def set_missing_values(source, target): target.purpose = "Material Transfer for Manufacture" From 3f45901f257f69d9e3155507da020c18c5155cdc Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 4 Jun 2021 11:16:38 +0530 Subject: [PATCH 085/180] fix: invalid 'depends_on' expression in opportunity (#25955) --- erpnext/crm/doctype/opportunity/opportunity.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 2e09a76c0f..4ba4140244 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -280,7 +280,6 @@ "read_only": 1 }, { - "depends_on": "eval:", "fieldname": "territory", "fieldtype": "Link", "label": "Territory", @@ -431,7 +430,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2021-01-06 19:42:46.190051", + "modified": "2021-06-04 10:11:22.831139", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", From 215516f81940c5e743380d4f97a13eda7d9969be Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 4 Jun 2021 13:09:14 +0530 Subject: [PATCH 086/180] fix: AttributeError: 'PurchaseReceiptItem' object has no attribute 'purchase_invoice' (#25902) (#25957) * fix: AttributeError: 'PurchaseReceiptItem' object has no attribute 'purchase_invoice' This error occurs when upgrading from erpnext 13.0.1 to 13.4.0 after typing bench update --patch --reset * fix(minor): use .get instead of getattr Co-authored-by: D Tim Cummings --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index f1292d8cbd..83ba324495 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -497,7 +497,7 @@ class PurchaseReceipt(BuyingController): def update_billing_status(self, update_modified=True): updated_pr = [self.name] for d in self.get("items"): - if d.purchase_invoice and d.purchase_invoice_item: + if d.get("purchase_invoice") and d.get("purchase_invoice_item"): d.db_set('billed_amt', d.amount, update_modified=update_modified) elif d.purchase_order_item: updated_pr += update_billed_amount_based_on_po(d.purchase_order_item, update_modified) @@ -748,4 +748,3 @@ def get_item_account_wise_additional_cost(purchase_document): account.base_amount * item.get(based_on_field) / total_item_cost return item_account_wise_cost - From 8a7e283926d51626d9114eaa4f6647b920ad8c9f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 4 Jun 2021 22:53:26 +0530 Subject: [PATCH 087/180] feat: Item Taxes based on net rate --- erpnext/controllers/taxes_and_totals.py | 4 +- .../public/js/controllers/taxes_and_totals.js | 90 ++++++++++++++++--- erpnext/public/js/controllers/transaction.js | 66 +------------- erpnext/stock/doctype/item/item.py | 6 ++ erpnext/stock/doctype/item_tax/item_tax.json | 20 ++++- erpnext/stock/get_item_details.py | 34 +++++-- 6 files changed, 137 insertions(+), 83 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 9fae49482d..8040435634 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -54,6 +54,7 @@ class calculate_taxes_and_totals(object): if item.item_code and item.get('item_tax_template'): item_doc = frappe.get_cached_doc("Item", item.item_code) args = { + 'net_rate': item.net_rate or item.rate, 'tax_category': self.doc.get('tax_category'), 'posting_date': self.doc.get('posting_date'), 'bill_date': self.doc.get('bill_date'), @@ -78,7 +79,8 @@ class calculate_taxes_and_totals(object): taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True) if item.item_tax_template not in taxes: - frappe.throw(_("Row {0}: Invalid Item Tax Template for item {1}").format( + item.item_tax_template = taxes[0] + frappe.msgprint(_("Row {0}: Item Tax template updated as per validity and rate applied").format( item.idx, frappe.bold(item.item_code) )) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 2e133bed2e..ff4898eeff 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -12,7 +12,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) { effective_item_rate = item.blanket_order_rate; } - if(item.margin_type == "Percentage"){ + if(item.margin_type == "Percentage") { item.rate_with_margin = flt(effective_item_rate) + flt(effective_item_rate) * ( flt(item.margin_rate_or_amount) / 100); } else { @@ -73,15 +73,18 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ }, _calculate_taxes_and_totals: function() { - this.validate_conversion_rate(); - this.calculate_item_values(); - this.initialize_taxes(); - this.determine_exclusive_rate(); - this.calculate_net_total(); - this.calculate_taxes(); - this.manipulate_grand_total_for_inclusive_tax(); - this.calculate_totals(); - this._cleanup(); + frappe.run_serially([ + () => this.validate_conversion_rate(), + () => this.calculate_item_values(), + () => this.update_item_tax_map(), + () => this.initialize_taxes(), + () => this.determine_exclusive_rate(), + () => this.calculate_net_total(), + () => this.calculate_taxes(), + () => this.manipulate_grand_total_for_inclusive_tax(), + () => this.calculate_totals(), + () => this._cleanup() + ]); }, validate_conversion_rate: function() { @@ -263,6 +266,68 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]); }, + update_item_tax_map: function() { + let me = this; + let item_codes = []; + let item_rates = {}; + $.each(this.frm.doc.items || [], function(i, item) { + if(item.item_code) { + // Use combination of name and item code in case same item is added multiple times + item_codes.push([item.item_code, item.name]); + item_rates[item.name] = item.net_rate; + } + }); + + if(item_codes.length) { + return this.frm.call({ + method: "erpnext.stock.get_item_details.get_item_tax_info", + args: { + company: me.frm.doc.company, + tax_category: cstr(me.frm.doc.tax_category), + item_codes: item_codes, + item_rates: item_rates + }, + callback: function(r) { + if(!r.exc) { + $.each(me.frm.doc.items || [], function(i, item) { + if(item.name && r.message.hasOwnProperty(item.name)) { + item.item_tax_template = r.message[item.name].item_tax_template; + item.item_tax_rate = r.message[item.name].item_tax_rate; + me.add_taxes_from_item_tax_template(item.item_tax_rate); + } + else { + item.item_tax_template = ""; + item.item_tax_rate = "{}"; + } + }); + } + + this.frm.refresh_fields(); + } + }); + } + }, + + add_taxes_from_item_tax_template: function(item_tax_map) { + let me = this; + + if(item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) { + if(typeof (item_tax_map) == "string") { + item_tax_map = JSON.parse(item_tax_map); + } + + $.each(item_tax_map, function(tax, rate) { + let found = (me.frm.doc.taxes || []).find(d => d.account_head === tax); + if(!found) { + let child = frappe.model.add_child(me.frm.doc, "taxes"); + child.charge_type = "On Net Total"; + child.account_head = tax; + child.rate = 0; + } + }); + } + }, + calculate_taxes: function() { var me = this; this.frm.doc.rounding_adjustment = 0; @@ -406,6 +471,11 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ let tax_detail = tax.item_wise_tax_detail; let key = item.item_code || item.item_name; + if(typeof (tax_detail) == "string") { + tax.item_wise_tax_detail = JSON.parse(tax.item_wise_tax_detail); + tax_detail = tax.item_wise_tax_detail; + } + let item_wise_tax_amount = current_tax_amount * this.frm.doc.conversion_rate; if (tax_detail && tax_detail[key]) item_wise_tax_amount += tax_detail[key][1]; diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index ad1976d2d2..ee6696b7d1 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -6,6 +6,7 @@ frappe.provide('erpnext.accounts.dimensions'); erpnext.TransactionController = erpnext.taxes_and_totals.extend({ setup: function() { this._super(); + let me = this; frappe.flags.hide_serial_batch_dialog = true; frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); @@ -43,8 +44,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ cur_frm.cscript.calculate_stock_uom_rate(frm, cdt, cdn); }); - - frappe.ui.form.on(this.frm.cscript.tax_table, "rate", function(frm, cdt, cdn) { cur_frm.cscript.calculate_taxes_and_totals(); }); @@ -121,7 +120,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }); - var me = this; if(this.frm.fields_dict["items"].grid.get_field('batch_no')) { this.frm.set_query("batch_no", "items", function(doc, cdt, cdn) { return me.set_query_for_batch(doc, cdt, cdn); @@ -556,6 +554,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ name: me.frm.doc.name, project: item.project || me.frm.doc.project, qty: item.qty || 1, + net_rate: item.rate, stock_qty: item.stock_qty, conversion_factor: item.conversion_factor, weight_per_unit: item.weight_per_unit, @@ -712,26 +711,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }); }, - add_taxes_from_item_tax_template: function(item_tax_map) { - let me = this; - - if(item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) { - if(typeof (item_tax_map) == "string") { - item_tax_map = JSON.parse(item_tax_map); - } - - $.each(item_tax_map, function(tax, rate) { - let found = (me.frm.doc.taxes || []).find(d => d.account_head === tax); - if(!found) { - let child = frappe.model.add_child(me.frm.doc, "taxes"); - child.charge_type = "On Net Total"; - child.account_head = tax; - child.rate = 0; - } - }); - } - }, - serial_no: function(doc, cdt, cdn) { var me = this; var item = frappe.get_doc(cdt, cdn); @@ -835,9 +814,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ frappe.run_serially([ () => me.frm.script_manager.trigger("currency"), - () => me.update_item_tax_map(), () => me.apply_default_taxes(), - () => me.apply_pricing_rule() + () => me.apply_pricing_rule(), + () => me.calculate_taxes_and_totals() ]); } } @@ -1816,7 +1795,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ callback: function(r) { if(!r.exc) { item.item_tax_rate = r.message; - me.add_taxes_from_item_tax_template(item.item_tax_rate); me.calculate_taxes_and_totals(); } } @@ -1827,43 +1805,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, - update_item_tax_map: function() { - var me = this; - var item_codes = []; - $.each(this.frm.doc.items || [], function(i, item) { - if(item.item_code) { - item_codes.push(item.item_code); - } - }); - if(item_codes.length) { - return this.frm.call({ - method: "erpnext.stock.get_item_details.get_item_tax_info", - args: { - company: me.frm.doc.company, - tax_category: cstr(me.frm.doc.tax_category), - item_codes: item_codes - }, - callback: function(r) { - if(!r.exc) { - $.each(me.frm.doc.items || [], function(i, item) { - if(item.item_code && r.message.hasOwnProperty(item.item_code)) { - if (!item.item_tax_template) { - item.item_tax_template = r.message[item.item_code].item_tax_template; - item.item_tax_rate = r.message[item.item_code].item_tax_rate; - } - me.add_taxes_from_item_tax_template(item.item_tax_rate); - } else { - item.item_tax_template = ""; - item.item_tax_rate = "{}"; - } - }); - me.calculate_taxes_and_totals(); - } - } - }); - } - }, is_recurring: function() { // set default values for recurring documents diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index dbac79465e..60f468e82e 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -128,6 +128,7 @@ class Item(WebsiteGenerator): self.validate_auto_reorder_enabled_in_stock_settings() self.cant_change() self.update_show_in_website() + self.validate_item_tax_net_rate_range() if not self.get("__islocal"): self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group") @@ -490,6 +491,11 @@ class Item(WebsiteGenerator): if self.disabled: self.show_in_website = False + def validate_item_tax_net_rate_range(self): + for tax in self.get('taxes'): + if tax.maximum_net_rate < tax.minimum_net_rate: + frappe.throw(_("Row #{0}: Maximum Net Rate cannot be greater than Minimum Net Rate")) + def update_template_tables(self): template = frappe.get_doc("Item", self.variant_of) diff --git a/erpnext/stock/doctype/item_tax/item_tax.json b/erpnext/stock/doctype/item_tax/item_tax.json index ae36efc7e3..fb100967f3 100644 --- a/erpnext/stock/doctype/item_tax/item_tax.json +++ b/erpnext/stock/doctype/item_tax/item_tax.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2013-02-22 01:28:01", "doctype": "DocType", "editable_grid": 1, @@ -6,7 +7,9 @@ "field_order": [ "item_tax_template", "tax_category", - "valid_from" + "valid_from", + "minimum_net_rate", + "maximum_net_rate" ], "fields": [ { @@ -33,11 +36,24 @@ "fieldtype": "Date", "in_list_view": 1, "label": "Valid From" + }, + { + "fieldname": "maximum_net_rate", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Maximum Net Rate" + }, + { + "fieldname": "minimum_net_rate", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Minimum Net Rate" } ], "idx": 1, "istable": 1, - "modified": "2020-06-25 01:40:28.859752", + "links": [], + "modified": "2021-06-03 13:20:06.982303", "modified_by": "Administrator", "module": "Stock", "name": "Item Tax", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index d1dcdc21c8..6f510539a4 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -436,18 +436,22 @@ def get_barcode_data(items_list): return itemwise_barcode @frappe.whitelist() -def get_item_tax_info(company, tax_category, item_codes): +def get_item_tax_info(company, tax_category, item_codes, item_rates=None): out = {} if isinstance(item_codes, string_types): item_codes = json.loads(item_codes) + if isinstance(item_rates, string_types): + item_rates = json.loads(item_rates) + for item_code in item_codes: - if not item_code or item_code in out: + if not item_code or item_code[1] in out: continue - out[item_code] = {} - item = frappe.get_cached_doc("Item", item_code) - get_item_tax_template({"company": company, "tax_category": tax_category}, item, out[item_code]) - out[item_code]["item_tax_rate"] = get_item_tax_map(company, out[item_code].get("item_tax_template"), as_json=True) + out[item_code[1]] = {} + item = frappe.get_cached_doc("Item", item_code[0]) + args = {"company": company, "tax_category": tax_category, "net_rate": item_rates[item_code[1]]} + get_item_tax_template(args, item, out[item_code[1]]) + out[item_code[1]]["item_tax_rate"] = get_item_tax_map(company, out[item_code[1]].get("item_tax_template"), as_json=True) return out @@ -478,12 +482,13 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False): for tax in taxes: tax_company = frappe.get_value("Item Tax Template", tax.item_tax_template, 'company') - if tax.valid_from and tax_company == args['company']: + if (tax.valid_from or tax.maximum_net_rate) and tax_company == args['company']: # In purchase Invoice first preference will be given to supplier invoice date # if supplier date is not present then posting date validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date') - if getdate(tax.valid_from) <= getdate(validation_date): + if getdate(tax.valid_from) <= getdate(validation_date) \ + and is_within_valid_range(args, tax): taxes_with_validity.append(tax) else: if tax_company == args['company']: @@ -502,12 +507,25 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False): if not taxes_with_validity and (not taxes_with_no_validity): return None + # do not change if already a valid template + if args.get('item_tax_template') in taxes: + return arg.get('item_tax_template') + for tax in taxes: if cstr(tax.tax_category) == cstr(args.get("tax_category")): out["item_tax_template"] = tax.item_tax_template return tax.item_tax_template return None +def is_within_valid_range(args, tax): + if not flt(tax.maximum_net_rate): + # No range specified, just ignore + return True + elif flt(tax.minimum_net_rate) <= flt(args.get('net_rate')) <= flt(tax.maximum_net_rate): + return True + + return False + @frappe.whitelist() def get_item_tax_map(company, item_tax_template, as_json=True): item_tax_map = {} From e8a78bd43d027ea36ff2902ecf48835b76109038 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 5 Jun 2021 13:16:39 +0530 Subject: [PATCH 088/180] fix: Add test cases --- .../sales_invoice/test_sales_invoice.py | 69 +++++++++++++------ erpnext/stock/doctype/item/item.py | 2 +- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index df6d483904..1b1bb3b18f 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1963,6 +1963,54 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(value_details['TotInvVal'], si.base_grand_total) self.assertTrue(einvoice['EwbDtls']) + def test_item_tax_validity(self): + item = frappe.get_doc("Item", "_Test Item 2") + + if item.taxes: + item.taxes = [] + item.save() + + item.append("taxes", { + "item_tax_template": "_Test Item Tax Template 1 - _TC", + "valid_from": add_days(nowdate(), 1) + }) + + item.save() + + sales_invoice = create_sales_invoice(item = "_Test Item 2", do_not_save=1) + sales_invoice.items[0].item_tax_template = "_Test Item Tax Template 1 - _TC" + self.assertRaises(frappe.ValidationError, sales_invoice.save) + + item.taxes = [] + item.save() + + def test_item_tax_net_range(self): + item = create_item("T Shirt") + + item.set('taxes', []) + item.append("taxes", { + "item_tax_template": "_Test Account Excise Duty @ 10 - _TC", + "minimum_net_rate": 0, + "maximum_net_rate": 500 + }) + + item.append("taxes", { + "item_tax_template": "_Test Account Excise Duty @ 12 - _TC", + "minimum_net_rate": 501, + "maximum_net_rate": 1000 + }) + + item.save() + + sales_invoice = create_sales_invoice(item = "T Shirt", rate=700, do_not_submit=True) + self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC") + + # Apply discount + sales_invoice.apply_discount_on = 'Net Total' + sales_invoice.discount_amount = 300 + sales_invoice.save() + self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC") + def make_test_address_for_ewaybill(): if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'): address = frappe.get_doc({ @@ -2085,27 +2133,6 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date): doc.assertEqual(expected_gle[i][2], gle.credit) doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) - def test_item_tax_validity(self): - item = frappe.get_doc("Item", "_Test Item 2") - - if item.taxes: - item.taxes = [] - item.save() - - item.append("taxes", { - "item_tax_template": "_Test Item Tax Template 1 - _TC", - "valid_from": add_days(nowdate(), 1) - }) - - item.save() - - sales_invoice = create_sales_invoice(item = "_Test Item 2", do_not_save=1) - sales_invoice.items[0].item_tax_template = "_Test Item Tax Template 1 - _TC" - self.assertRaises(frappe.ValidationError, sales_invoice.save) - - item.taxes = [] - item.save() - def create_sales_invoice(**args): si = frappe.new_doc("Sales Invoice") args = frappe._dict(args) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 60f468e82e..0420ea7c54 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -493,7 +493,7 @@ class Item(WebsiteGenerator): def validate_item_tax_net_rate_range(self): for tax in self.get('taxes'): - if tax.maximum_net_rate < tax.minimum_net_rate: + if flt(tax.maximum_net_rate) < flt(tax.minimum_net_rate): frappe.throw(_("Row #{0}: Maximum Net Rate cannot be greater than Minimum Net Rate")) def update_template_tables(self): From 3646c301edc1380efcd0eedcd590e8124dfe90d8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 5 Jun 2021 13:21:03 +0530 Subject: [PATCH 089/180] fix: Linting issues --- .../public/js/controllers/taxes_and_totals.js | 25 +++++++++---------- erpnext/stock/get_item_details.py | 2 +- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index ff4898eeff..0b9d771119 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -12,7 +12,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) { effective_item_rate = item.blanket_order_rate; } - if(item.margin_type == "Percentage") { + if (item.margin_type == "Percentage") { item.rate_with_margin = flt(effective_item_rate) + flt(effective_item_rate) * ( flt(item.margin_rate_or_amount) / 100); } else { @@ -22,7 +22,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ item_rate = flt(item.rate_with_margin , precision("rate", item)); - if(item.discount_percentage){ + if (item.discount_percentage) { item.discount_amount = flt(item.rate_with_margin) * flt(item.discount_percentage) / 100; } @@ -271,14 +271,14 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ let item_codes = []; let item_rates = {}; $.each(this.frm.doc.items || [], function(i, item) { - if(item.item_code) { + if (item.item_code) { // Use combination of name and item code in case same item is added multiple times item_codes.push([item.item_code, item.name]); item_rates[item.name] = item.net_rate; } }); - if(item_codes.length) { + if (item_codes.length) { return this.frm.call({ method: "erpnext.stock.get_item_details.get_item_tax_info", args: { @@ -288,21 +288,18 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ item_rates: item_rates }, callback: function(r) { - if(!r.exc) { + if (!r.exc) { $.each(me.frm.doc.items || [], function(i, item) { - if(item.name && r.message.hasOwnProperty(item.name)) { + if (item.name && r.message.hasOwnProperty(item.name)) { item.item_tax_template = r.message[item.name].item_tax_template; item.item_tax_rate = r.message[item.name].item_tax_rate; me.add_taxes_from_item_tax_template(item.item_tax_rate); - } - else { + } else { item.item_tax_template = ""; item.item_tax_rate = "{}"; } }); } - - this.frm.refresh_fields(); } }); } @@ -311,14 +308,14 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ add_taxes_from_item_tax_template: function(item_tax_map) { let me = this; - if(item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) { - if(typeof (item_tax_map) == "string") { + if (item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) { + if (typeof (item_tax_map) == "string") { item_tax_map = JSON.parse(item_tax_map); } $.each(item_tax_map, function(tax, rate) { let found = (me.frm.doc.taxes || []).find(d => d.account_head === tax); - if(!found) { + if (!found) { let child = frappe.model.add_child(me.frm.doc, "taxes"); child.charge_type = "On Net Total"; child.account_head = tax; @@ -632,6 +629,8 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail); }); } + + this.frm.refresh_fields(); }, set_discount_amount: function() { diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 6f510539a4..746cbbf601 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -509,7 +509,7 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False): # do not change if already a valid template if args.get('item_tax_template') in taxes: - return arg.get('item_tax_template') + return args.get('item_tax_template') for tax in taxes: if cstr(tax.tax_category) == cstr(args.get("tax_category")): From a85c2c16b42d0ba5bc5f0157eaf87a062052ab6b Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sun, 6 Jun 2021 11:07:20 +0530 Subject: [PATCH 090/180] fix(pos): item rate precision in item selector --- erpnext/selling/page/point_of_sale/pos_item_selector.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 3e3377e5dc..64c529ee4a 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -80,6 +80,7 @@ erpnext.PointOfSale.ItemSelector = class { // eslint-disable-next-line no-unused-vars const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom, price_list_rate } = item; const indicator_color = actual_qty > 10 ? "green" : actual_qty <= 0 ? "red" : "orange"; + const precision = flt(price_list_rate, 2) % 1 != 0 ? 2 : 0; let qty_to_display = actual_qty; @@ -121,7 +122,7 @@ erpnext.PointOfSale.ItemSelector = class {
${frappe.ellipsis(item.item_name, 18)}
-
${format_currency(price_list_rate, item.currency, 0) || 0}
+
${format_currency(price_list_rate, item.currency, precision) || 0}
` ); From bc46a9772d7daaa50d0ecdac301e604cb43b5878 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sun, 6 Jun 2021 11:11:07 +0530 Subject: [PATCH 091/180] fix(pos): cash shortcuts not working --- erpnext/selling/page/point_of_sale/pos_payment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 600f160490..156fb777fe 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -171,7 +171,7 @@ erpnext.PointOfSale.Payment = class { this.setup_listener_for_payments(); - this.$payment_modes.on('click', '.shortcut', () => { + this.$payment_modes.on('click', '.shortcut', function() { const value = $(this).attr('data-value'); me.selected_mode.set_value(value); }); From 3f32969bee847e9a3abfc89ae2c7c84c07a7e53e Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sun, 6 Jun 2021 11:18:20 +0530 Subject: [PATCH 092/180] fix(pos): broken image in item details section --- .../selling/page/point_of_sale/pos_item_details.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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 df62696c4b..5e09df8efe 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_details.js +++ b/erpnext/selling/page/point_of_sale/pos_item_details.js @@ -133,13 +133,24 @@ erpnext.PointOfSale.ItemDetails = class { this.$item_description.html(get_description_html()); this.$item_price.html(format_currency(price_list_rate, this.currency)); if (image) { - this.$item_image.html(`${image}`); + this.$item_image.html( + `${frappe.get_abbr(item_name)}` + ); } else { this.$item_image.html(`
${frappe.get_abbr(item_name)}
`); } } + handle_broken_image($img) { + const item_abbr = $($img).attr('alt'); + $($img).replaceWith(`
${item_abbr}
`); + } + render_discount_dom(item) { if (item.discount_percentage) { this.$dicount_section.html( From 18be767f75b9c858fa66294d037f1a9b9d2c6ace Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 6 Jun 2021 13:25:34 +0530 Subject: [PATCH 093/180] fix: Remove invalid test --- .../sales_invoice/test_sales_invoice.py | 21 ------------------- erpnext/controllers/taxes_and_totals.py | 11 +++++----- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 1b1bb3b18f..0d61f67e52 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1963,27 +1963,6 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(value_details['TotInvVal'], si.base_grand_total) self.assertTrue(einvoice['EwbDtls']) - def test_item_tax_validity(self): - item = frappe.get_doc("Item", "_Test Item 2") - - if item.taxes: - item.taxes = [] - item.save() - - item.append("taxes", { - "item_tax_template": "_Test Item Tax Template 1 - _TC", - "valid_from": add_days(nowdate(), 1) - }) - - item.save() - - sales_invoice = create_sales_invoice(item = "_Test Item 2", do_not_save=1) - sales_invoice.items[0].item_tax_template = "_Test Item Tax Template 1 - _TC" - self.assertRaises(frappe.ValidationError, sales_invoice.save) - - item.taxes = [] - item.save() - def test_item_tax_net_range(self): item = create_item("T Shirt") diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 8040435634..fb22a1d608 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -78,11 +78,12 @@ class calculate_taxes_and_totals(object): taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True) - if item.item_tax_template not in taxes: - item.item_tax_template = taxes[0] - frappe.msgprint(_("Row {0}: Item Tax template updated as per validity and rate applied").format( - item.idx, frappe.bold(item.item_code) - )) + if taxes: + if item.item_tax_template not in taxes: + item.item_tax_template = taxes[0] + frappe.msgprint(_("Row {0}: Item Tax template updated as per validity and rate applied").format( + item.idx, frappe.bold(item.item_code) + )) def validate_conversion_rate(self): # validate conversion rate From 7a20c3b92ae05395bf75703a1e6aaad31f537a79 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 6 Jun 2021 19:23:21 +0530 Subject: [PATCH 094/180] fix: Ignore internal transfer inoices from GST Reports --- erpnext/regional/report/gstr_1/gstr_1.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index b7c096248f..80e2d725a2 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -147,6 +147,13 @@ class Gstr1Report(object): def get_invoice_data(self): self.invoices = frappe._dict() conditions = self.get_conditions() + + company_gstins = get_company_gstin_number(self.filters.get('company'), all_gstins=True) + + self.filters.update({ + 'company_gstins': company_gstins + }) + invoice_data = frappe.db.sql(""" select {select_columns} @@ -193,6 +200,9 @@ class Gstr1Report(object): elif self.filters.get("type_of_business") == "EXPORT": conditions += """ AND is_return !=1 and gst_category = 'Overseas' """ + + conditions += " AND billing_address_gstin NOT IN %(company_gstins)s" + return conditions def get_invoice_items(self): @@ -810,7 +820,8 @@ def get_rate_and_tax_details(row, gstin): return {"num": int(num), "itm_det": itm_det} -def get_company_gstin_number(company, address=None): +def get_company_gstin_number(company, address=None, all_gstins=False): + gstin = '' if address: gstin = frappe.db.get_value("Address", address, "gstin") @@ -822,9 +833,9 @@ def get_company_gstin_number(company, address=None): ["Dynamic Link", "parenttype", "=", "Address"], ] gstin = frappe.get_all("Address", filters=filters, pluck="gstin") - if gstin: - gstin[0] - + if gstin and not all_gstins: + gstin = gstin[0] + if not gstin: address = frappe.bold(address) if address else "" frappe.throw(_("Please set valid GSTIN No. in Company Address {} for company {}").format( From d4398fd84aee28ef82c98ab44d42082550195c39 Mon Sep 17 00:00:00 2001 From: Anuja Pawar <60467153+Anuja-pawar@users.noreply.github.com> Date: Mon, 7 Jun 2021 16:20:21 +0530 Subject: [PATCH 095/180] fix: update cost center from pos (#25971) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 023f4b049c..f8b5179d2c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -531,7 +531,7 @@ class SalesInvoice(SellingController): # set pos values in items for item in self.get("items"): if item.get('item_code'): - profile_details = get_pos_profile_item_details(pos, frappe._dict(item.as_dict()), pos) + profile_details = get_pos_profile_item_details(pos, frappe._dict(item.as_dict()), pos, update_data=True) for fname, val in iteritems(profile_details): if (not for_validate) or (for_validate and not item.get(fname)): item.set(fname, val) From 447c978757ffeef8ebaeedc2dd34124ed9b45fff Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 28 May 2021 21:28:42 +0530 Subject: [PATCH 096/180] fix: choose correct Salary Structure Assignment when getting data for formula eval --- .../doctype/salary_slip/salary_slip.py | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index afdf081ac8..cc9e8d1043 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -493,8 +493,28 @@ class SalarySlip(TransactionBase): '''Returns data for evaluating formula''' data = frappe._dict() + salary_structure_assignment = frappe.get_value( + "Salary Structure Assignment", + { + "employee": self.employee, + "salary_structure": self.salary_structure, + "from_date": ("<=", self.start_date), + "docstatus": 1, + }, + order_by="from_date desc", + ) + + if not salary_structure_assignment: + frappe.throw( + _("Please assign a Salary Structure for Employee {0} " + "applicable from or before {1} first").format( + frappe.bold(self.employee_name), + frappe.bold(self.start_date) + ) + ) + data.update(frappe.get_doc("Salary Structure Assignment", - {"employee": self.employee, "salary_structure": self.salary_structure}).as_dict()) + salary_structure_assignment).as_dict()) data.update(frappe.get_doc("Employee", self.employee).as_dict()) data.update(self.as_dict()) From 74818c7b624534a39b647476587abb02f84b1e88 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sat, 29 May 2021 00:04:26 +0530 Subject: [PATCH 097/180] fix: improve filter for `from_date`; validation for joining and relieving date --- .../doctype/salary_slip/salary_slip.py | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index cc9e8d1043..2b35d94dfc 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -115,10 +115,23 @@ class SalarySlip(TransactionBase): status = "Cancelled" return status - def validate_dates(self): + def validate_dates(self, joining_date=None, relieving_date=None): if date_diff(self.end_date, self.start_date) < 0: frappe.throw(_("To date cannot be before From date")) + if not joining_date: + joining_date, relieving_date = frappe.get_cached_value( + "Employee", + self.employee, + ("date_of_joining", "relieving_date") + ) + + if date_diff(self.end_date, joining_date) < 0: + frappe.throw(_("Cannot create Salary Slip for Employee joining after Payroll Period")) + + if relieving_date and date_diff(relieving_date, self.start_date) < 0: + frappe.throw(_("Cannot create Salary Slip for Employee who has left before Payroll Period")) + def is_rounding_total_disabled(self): return cint(frappe.db.get_single_value("Payroll Settings", "disable_rounded_total")) @@ -154,9 +167,14 @@ class SalarySlip(TransactionBase): if not self.salary_slip_based_on_timesheet: self.get_date_details() - self.validate_dates() - joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee, - ["date_of_joining", "relieving_date"]) + + joining_date, relieving_date = frappe.get_cached_value( + "Employee", + self.employee, + ("date_of_joining", "relieving_date") + ) + + self.validate_dates(joining_date, relieving_date) #getin leave details self.get_working_days_details(joining_date, relieving_date) @@ -492,13 +510,21 @@ class SalarySlip(TransactionBase): def get_data_for_eval(self): '''Returns data for evaluating formula''' data = frappe._dict() + employee = frappe.get_doc("Employee", self.employee).as_dict() + + start_date = getdate(self.start_date) + date_to_validate = ( + employee.date_of_joining + if employee.date_of_joining > start_date + else start_date + ) salary_structure_assignment = frappe.get_value( "Salary Structure Assignment", { "employee": self.employee, "salary_structure": self.salary_structure, - "from_date": ("<=", self.start_date), + "from_date": ("<=", date_to_validate), "docstatus": 1, }, order_by="from_date desc", @@ -509,14 +535,14 @@ class SalarySlip(TransactionBase): _("Please assign a Salary Structure for Employee {0} " "applicable from or before {1} first").format( frappe.bold(self.employee_name), - frappe.bold(self.start_date) + frappe.bold(formatdate(date_to_validate)), ) ) data.update(frappe.get_doc("Salary Structure Assignment", salary_structure_assignment).as_dict()) - data.update(frappe.get_doc("Employee", self.employee).as_dict()) + data.update(employee) data.update(self.as_dict()) # set values for components From 0e5e1350b241e9ac0b1c93b92da1d8999cd9723c Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 4 Jun 2021 13:51:45 +0530 Subject: [PATCH 098/180] perf: use frappe.get_value with wildcard instead of another frappe.get_doc call --- erpnext/payroll/doctype/salary_slip/salary_slip.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 2b35d94dfc..877503b41c 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -527,7 +527,9 @@ class SalarySlip(TransactionBase): "from_date": ("<=", date_to_validate), "docstatus": 1, }, + "*", order_by="from_date desc", + as_dict=True, ) if not salary_structure_assignment: @@ -539,9 +541,7 @@ class SalarySlip(TransactionBase): ) ) - data.update(frappe.get_doc("Salary Structure Assignment", - salary_structure_assignment).as_dict()) - + data.update(salary_structure_assignment) data.update(employee) data.update(self.as_dict()) From ca205be5ac62ff25815df81bde6e2a8a7852b26c Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Mon, 7 Jun 2021 18:40:54 +0530 Subject: [PATCH 099/180] fix: tests --- .../doctype/salary_slip/test_salary_slip.py | 51 +++++++++++-------- .../salary_structure/test_salary_structure.py | 39 +++++++++----- 2 files changed, 57 insertions(+), 33 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index 01e4170d31..9e7db977ab 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -8,7 +8,6 @@ import erpnext import calendar import random from erpnext.accounts.utils import get_fiscal_year -from frappe.utils.make_random import get_random from frappe.utils import getdate, nowdate, add_days, add_months, flt, get_first_day, get_last_day, cstr from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_details @@ -155,12 +154,14 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.gross_pay, 78000) def test_payment_days(self): + from erpnext.payroll.doctype.salary_structure.test_salary_structure import create_salary_structure_assignment + no_of_days = self.get_no_of_days() # Holidays not included in working days frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1) # set joinng date in the same month - make_employee("test_payment_days@salary.com") + employee = make_employee("test_payment_days@salary.com") if getdate(nowdate()).day >= 15: relieving_date = getdate(add_days(nowdate(),-10)) date_of_joining = getdate(add_days(nowdate(),-10)) @@ -174,25 +175,30 @@ class TestSalarySlip(unittest.TestCase): date_of_joining = getdate(nowdate()) relieving_date = getdate(nowdate()) - frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_payment_days@salary.com"}, "name"), "date_of_joining", date_of_joining) - frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_payment_days@salary.com"}, "name"), "relieving_date", None) - frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_payment_days@salary.com"}, "name"), "status", "Active") + frappe.db.set_value("Employee", employee, { + "date_of_joining": date_of_joining, + "relieving_date": None, + "status": "Active" + }) - ss = make_employee_salary_slip("test_payment_days@salary.com", "Monthly", "Test Payment Days") + salary_structure = "Test Payment Days" + ss = make_employee_salary_slip("test_payment_days@salary.com", "Monthly", salary_structure) self.assertEqual(ss.total_working_days, no_of_days[0]) self.assertEqual(ss.payment_days, (no_of_days[0] - getdate(date_of_joining).day + 1)) # set relieving date in the same month - frappe.db.set_value("Employee",frappe.get_value("Employee", - {"employee_name":"test_payment_days@salary.com"}, "name"), "date_of_joining", (add_days(nowdate(),-60))) - frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_payment_days@salary.com"}, "name"), "relieving_date", relieving_date) - frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_payment_days@salary.com"}, "name"), "status", "Left") + frappe.db.set_value("Employee", employee, { + "date_of_joining": add_days(nowdate(),-60), + "relieving_date": relieving_date, + "status": "Left" + }) + + if date_of_joining.day > 1: + self.assertRaises(frappe.ValidationError, ss.save) + + create_salary_structure_assignment(employee, salary_structure) + ss.reload() ss.save() self.assertEqual(ss.total_working_days, no_of_days[0]) @@ -285,6 +291,7 @@ class TestSalarySlip(unittest.TestCase): def test_multi_currency_salary_slip(self): from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + applicant = make_employee("test_multi_currency_salary_slip@salary.com", company="_Test Company") frappe.db.sql("""delete from `tabSalary Structure` where name='Test Multi Currency Salary Slip'""") salary_structure = make_salary_structure("Test Multi Currency Salary Slip", "Monthly", employee=applicant, company="_Test Company", currency='USD') @@ -325,7 +332,8 @@ class TestSalarySlip(unittest.TestCase): def test_component_wise_year_to_date_computation(self): from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - applicant = make_employee("test_ytd@salary.com", company="_Test Company") + employee_name = "test_component_wise_ytd@salary.com" + applicant = make_employee(employee_name, company="_Test Company") payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company") @@ -336,13 +344,13 @@ class TestSalarySlip(unittest.TestCase): "Monthly", employee=applicant, company="_Test Company", currency="INR", payroll_period=payroll_period) # clear salary slip for this employee - frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = 'test_ytd@salary.com'") + frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = '%s'" % employee_name) create_salary_slips_for_payroll_period(applicant, salary_structure.name, payroll_period, deduct_random=False, num=3) salary_slips = frappe.get_all("Salary Slip", fields=["name"], filters={"employee_name": - "test_ytd@salary.com"}, order_by = "posting_date") + employee_name}, order_by="posting_date") year_to_date = dict() for slip in salary_slips: @@ -380,10 +388,10 @@ class TestSalarySlip(unittest.TestCase): from erpnext.payroll.doctype.salary_structure.test_salary_structure import \ make_salary_structure, create_salary_structure_assignment + salary_structure = make_salary_structure("Stucture to test tax", "Monthly", - other_details={"max_benefits": 100000}, test_tax=True) - create_salary_structure_assignment(employee, salary_structure.name, - payroll_period.start_date) + other_details={"max_benefits": 100000}, test_tax=True, + employee=employee, payroll_period=payroll_period) # create salary slip for whole period deducting tax only on last period # to find the total tax amount paid @@ -469,6 +477,7 @@ class TestSalarySlip(unittest.TestCase): def make_employee_salary_slip(user, payroll_frequency, salary_structure=None): from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + if not salary_structure: salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip" diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py index 36387f23df..26cd9922e4 100644 --- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py @@ -6,7 +6,7 @@ import frappe import unittest import erpnext from frappe.utils.make_random import get_random -from frappe.utils import nowdate, add_days, add_years, getdate, add_months +from frappe.utils import nowdate, add_days, add_years, getdate, add_months, get_first_day, date_diff from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\ make_deduction_salary_component, make_employee_salary_slip, create_tax_slab @@ -113,8 +113,9 @@ class TestSalaryStructure(unittest.TestCase): sal_struct = make_salary_structure("Salary Structure Multi Currency", "Monthly", currency='USD') self.assertEqual(sal_struct.currency, 'USD') -def make_salary_structure(salary_structure, payroll_frequency, employee=None, dont_submit=False, other_details=None, - test_tax=False, company=None, currency=erpnext.get_default_currency(), payroll_period=None): +def make_salary_structure(salary_structure, payroll_frequency, employee=None, + from_date=None, dont_submit=False, other_details=None,test_tax=False, + company=None, currency=erpnext.get_default_currency(), payroll_period=None): if test_tax: frappe.db.sql("""delete from `tabSalary Structure` where name=%s""",(salary_structure)) @@ -139,10 +140,23 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do else: salary_structure_doc = frappe.get_doc("Salary Structure", salary_structure) + filters = {'employee':employee, 'docstatus': 1} + if not from_date and payroll_period: + from_date = payroll_period.start_date + + if from_date: + filters['from_date'] = from_date + if employee and not frappe.db.get_value("Salary Structure Assignment", - {'employee':employee, 'docstatus': 1}) and salary_structure_doc.docstatus==1: - create_salary_structure_assignment(employee, salary_structure, company=company, currency=currency, - payroll_period=payroll_period) + filters) and salary_structure_doc.docstatus==1: + create_salary_structure_assignment( + employee, + salary_structure, + from_date=from_date, + company=company, + currency=currency, + payroll_period=payroll_period + ) return salary_structure_doc @@ -165,12 +179,13 @@ def create_salary_structure_assignment(employee, salary_structure, from_date=Non salary_structure_assignment.base = 50000 salary_structure_assignment.variable = 5000 - if getdate(nowdate()).day == 1: - date = from_date or nowdate() - else: - date = from_date or add_days(nowdate(), -1) + if not from_date: + from_date = get_first_day(nowdate()) + joining_date = frappe.get_cached_value("Employee", employee, "date_of_joining") + if date_diff(joining_date, from_date) > 0: + from_date = joining_date - salary_structure_assignment.from_date = date + salary_structure_assignment.from_date = from_date salary_structure_assignment.salary_structure = salary_structure salary_structure_assignment.currency = currency salary_structure_assignment.payroll_payable_account = get_payable_account(company) @@ -183,4 +198,4 @@ def create_salary_structure_assignment(employee, salary_structure, from_date=Non def get_payable_account(company=None): if not company: company = erpnext.get_default_company() - return frappe.db.get_value("Company", company, "default_payroll_payable_account") \ No newline at end of file + return frappe.db.get_value("Company", company, "default_payroll_payable_account") From 062e247353a2277727050c648a4633a2d24fb1b8 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Mon, 7 Jun 2021 19:34:02 +0530 Subject: [PATCH 100/180] test: remove unused imports --- .../payroll/doctype/salary_structure/test_salary_structure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py index 26cd9922e4..dce6b7aa3d 100644 --- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py @@ -6,7 +6,7 @@ import frappe import unittest import erpnext from frappe.utils.make_random import get_random -from frappe.utils import nowdate, add_days, add_years, getdate, add_months, get_first_day, date_diff +from frappe.utils import nowdate, add_years, get_first_day, date_diff from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\ make_deduction_salary_component, make_employee_salary_slip, create_tax_slab From e4fcc5562f3e4a26177eacc9d455596da1135aa8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 8 Jun 2021 16:35:47 +0530 Subject: [PATCH 101/180] refactor: Advance taxes and charges calculation --- .../advance_taxes_and_charges.json | 11 +- .../doctype/payment_entry/payment_entry.js | 162 +++++++++++++---- .../doctype/payment_entry/payment_entry.py | 163 +++++++++++++----- 3 files changed, 255 insertions(+), 81 deletions(-) diff --git a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json index c127601259..3e4e679c91 100644 --- a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json +++ b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json @@ -19,6 +19,7 @@ "section_break_8", "rate", "section_break_9", + "currency", "tax_amount", "total", "allocated_amount", @@ -172,12 +173,20 @@ "fieldtype": "Currency", "label": "Allocated Amount (Company Currency)", "options": "Company:company:default_currency" + }, + { + "fetch_from": "account_head.account_currency", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Account Currency", + "options": "Currency", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-05-31 02:03:57.444647", + "modified": "2021-06-08 07:04:44.287002", "modified_by": "Administrator", "module": "Accounts", "name": "Advance Taxes and Charges", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index b1cf9e02e6..6a3b717b12 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -1071,7 +1071,7 @@ frappe.ui.form.on('Payment Entry', { } me.frm.add_child("taxes", tax); } - frm.trigger('calculate_taxes'); + frm.events.apply_taxes(frm); frm.events.set_unallocated_amount(frm); } } @@ -1079,52 +1079,112 @@ frappe.ui.form.on('Payment Entry', { }); }, + apply_taxes: function(frm) { + frm.events.initialize_taxes(frm); + frm.events.determine_exclusive_rate(frm); + frm.events.calculate_taxes(frm); + }, + + initialize_taxes: function(frm) { + $.each(frm.doc["taxes"] || [], function(i, tax) { + tax.item_wise_tax_detail = {}; + let tax_fields = ["total", "tax_fraction_for_current_item", + "grand_total_fraction_for_current_item"]; + + if (cstr(tax.charge_type) != "Actual") { + tax_fields.push("tax_amount"); + } + + $.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0; }); + + frm.doc.paid_amount_after_tax = frm.doc.paid_amount; + }); + }, + + determine_exclusive_rate: function(frm) { + let has_inclusive_tax = false; + $.each(frm.doc["taxes"] || [], function(i, row) { + if(cint(row.included_in_paid_amount)) has_inclusive_tax = true; + }); + if(has_inclusive_tax==false) return; + + let cumulated_tax_fraction = 0.0; + $.each(frm.doc["taxes"] || [], function(i, tax) { + let current_tax_fraction = frm.events.get_current_tax_fraction(frm, tax); + tax.tax_fraction_for_current_item = current_tax_fraction[0]; + + if(i==0) { + tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item; + } else { + tax.grand_total_fraction_for_current_item = + me.frm.doc["taxes"][i-1].grand_total_fraction_for_current_item + + tax.tax_fraction_for_current_item; + } + + cumulated_tax_fraction += tax.tax_fraction_for_current_item; + frm.doc.paid_amount_after_tax = flt(frm.doc.paid_amount/(1+cumulated_tax_fraction)) + }); + }, + + get_current_tax_fraction: function(frm, tax) { + let current_tax_fraction = 0.0; + + if(cint(tax.included_in_paid_amount)) { + let tax_rate = tax.rate; + + if (tax.charge_type == "Actual") { + current_tax_fraction = tax.tax_amount/frm.doc.paid_amount_after_tax; + } else if(tax.charge_type == "On Paid Amount") { + current_tax_fraction = (tax_rate / 100.0); + } else if(tax.charge_type == "On Previous Row Amount") { + current_tax_fraction = (tax_rate / 100.0) * + frm.doc["taxes"][cint(tax.row_id) - 1].tax_fraction_for_current_item; + } else if(tax.charge_type == "On Previous Row Total") { + current_tax_fraction = (tax_rate / 100.0) * + frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_fraction_for_current_item; + } + } + + if(tax.add_deduct_tax && tax.add_deduct_tax == "Deduct") { + current_tax_fraction *= -1; + inclusive_tax_amount_per_qty *= -1; + } + return current_tax_fraction; + }, + + calculate_taxes: function(frm) { frm.doc.total_taxes_and_charges = 0.0; frm.doc.base_total_taxes_and_charges = 0.0; - $.each(me.frm.doc["taxes"] || [], function(i, tax) { - let tax_rate = tax.rate; - let current_tax_amount = 0.0; + let actual_tax_dict = {}; - // To set row_id by default as previous row. - if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) { - if (tax.idx === 1) { - frappe.throw(__("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")); - } - if (!tax.row_id) { - tax.row_id = tax.idx - 1; - } + // maintain actual tax rate based on idx + $.each(frm.doc["taxes"] || [], function(i, tax) { + if (tax.charge_type == "Actual") { + actual_tax_dict[tax.idx] = flt(tax.tax_amount, precision("tax_amount", tax)); } + }); - if(tax.charge_type == "Actual") { - current_tax_amount = flt(tax.tax_amount, precision("tax_amount", tax)); - } else if(tax.charge_type == "On Paid Amount") { - current_tax_amount = (tax_rate / 100.0) * frm.doc.paid_amount; - } else if(tax.charge_type == "On Previous Row Amount") { - current_tax_amount = (tax_rate / 100.0) * - frm.doc["taxes"][cint(tax.row_id) - 1].tax_amount; + $.each(me.frm.doc["taxes"] || [], function(i, tax) { + let current_tax_amount = frm.events.get_current_tax_amount(frm, tax); - } else if(tax.charge_type == "On Previous Row Total") { - current_tax_amount = (tax_rate / 100.0) * - frm.doc["taxes"][cint(tax.row_id) - 1].total; + // Adjust divisional loss to the last item + if (tax.charge_type == "Actual") { + actual_tax_dict[tax.idx] -= current_tax_amount; + if (i == frm.doc["taxes"].length - 1) { + current_tax_amount += actual_tax_dict[tax.idx]; + } } tax.tax_amount = current_tax_amount; tax.base_tax_amount = tax.tax_amount * frm.doc.source_exchange_rate; - current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; - let applicable_tax_amount = 0 - - if (!tax.included_in_paid_amount) { - applicable_tax_amount = current_tax_amount - } - if(i==0) { - tax.total = flt(frm.doc.paid_amount + applicable_tax_amount, precision("total", tax)); + tax.total = flt(frm.doc.paid_amount_after_tax + current_tax_amount, precision("total", tax)); } else { - tax.total = flt(frm.doc["taxes"][i-1].total + applicable_tax_amount, precision("total", tax)); + tax.total = flt(frm.doc["taxes"][i-1].total + current_tax_amount, precision("total", tax)); } tax.base_total = tax.total * frm.doc.source_exchange_rate; @@ -1136,6 +1196,36 @@ frappe.ui.form.on('Payment Entry', { frm.refresh_field('base_total_taxes_and_charges'); }); }, + + get_current_tax_amount: function(frm, tax) { + let tax_rate = tax.rate; + let current_tax_amount = 0.0; + + // To set row_id by default as previous row. + if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) { + if (tax.idx === 1) { + frappe.throw( + __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")); + } + if (!tax.row_id) { + tax.row_id = tax.idx - 1; + } + } + if(tax.charge_type == "Actual") { + current_tax_amount = flt(tax.tax_amount, precision("tax_amount", tax)) + } else if(tax.charge_type == "On Paid Amount") { + current_tax_amount = flt((tax_rate / 100.0) * frm.doc.paid_amount_after_tax); + } else if(tax.charge_type == "On Previous Row Amount") { + current_tax_amount = flt((tax_rate / 100.0) * + frm.doc["taxes"][cint(tax.row_id) - 1].tax_amount); + + } else if(tax.charge_type == "On Previous Row Total") { + current_tax_amount = flt((tax_rate / 100.0) * + frm.doc["taxes"][cint(tax.row_id) - 1].total); + } + + return current_tax_amount; + }, }); @@ -1184,27 +1274,27 @@ frappe.ui.form.on('Payment Entry Reference', { frappe.ui.form.on('Advance Taxes and Charges', { rate: function(frm) { - frm.events.calculate_taxes(frm); + frm.events.apply_taxes(frm); frm.events.set_unallocated_amount(frm); }, tax_amount : function(frm) { - frm.events.calculate_taxes(frm); + frm.events.apply_taxes(frm); frm.events.set_unallocated_amount(frm); }, row_id: function(frm) { - frm.events.calculate_taxes(frm); + frm.events.apply_taxes(frm); frm.events.set_unallocated_amount(frm); }, taxes_remove: function(frm) { - frm.events.calculate_taxes(frm); + frm.events.apply_taxes(frm); frm.events.set_unallocated_amount(frm); }, included_in_paid_amount: function(frm) { - frm.events.calculate_taxes(frm); + frm.events.apply_taxes(frm); frm.events.set_unallocated_amount(frm); } }) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index d960156670..83d52fa5f6 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -54,7 +54,7 @@ class PaymentEntry(AccountsController): self.validate_mandatory() self.validate_reference_documents() self.set_tax_withholding() - self.calculate_taxes() + self.apply_taxes() self.set_amounts() self.clear_unallocated_reference_document_rows() self.validate_payment_against_negative_invoice() @@ -65,7 +65,6 @@ class PaymentEntry(AccountsController): self.validate_allocated_amount() self.validate_paid_invoices() self.ensure_supplier_is_not_blocked() - self.set_advance_tax_account() self.set_status() def on_submit(self): @@ -311,14 +310,6 @@ class PaymentEntry(AccountsController): + "

" + _("If this is undesirable please cancel the corresponding Payment Entry."), title=_("Warning"), indicator="orange") - def set_advance_tax_account(self): - if self.get('taxes') and not self.advance_tax_account: - unrealized_profit_loss_account = frappe.db.get_value('Company', self.company, 'unrealized_profit_loss_account') - if not unrealized_profit_loss_account: - frappe.throw(_("Please select advance tax account or add Unrealized Profit / Loss Account in company master")) - - self.advance_tax_account = unrealized_profit_loss_account - def validate_journal_entry(self): for d in self.get("references"): if d.allocated_amount and d.reference_doctype == "Journal Entry": @@ -461,6 +452,11 @@ class PaymentEntry(AccountsController): for d in to_remove: self.remove(d) + def apply_taxes(self): + self.initialize_taxes() + self.determine_exclusive_rate() + self.calculate_taxes() + def set_amounts(self): self.set_received_amount() self.set_amounts_in_company_currency() @@ -715,12 +711,12 @@ class PaymentEntry(AccountsController): if account_currency != self.company_currency: frappe.throw(_("Currency for {0} must be {1}").format(d.account_head, self.company_currency)) - if self.payment_type == 'Pay': + if (self.payment_type == 'Pay' and self.advance_tax_account) or self.payment_type == 'Receive': dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit" - rev_dr_cr = "credit" if d.add_deduct_tax == "Add" else "debit" - elif self.payment_type == 'Receive': + elif (self.payment_type == 'Receive' and self.advance_tax_account) or self.payment_type == 'Pay': dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit" - rev_dr_cr = "debit" if d.add_deduct_tax == "Add" else "credit" + + payment_or_advance_account = self.get_party_account_for_taxes() gl_entries.append( self.get_gl_dict({ @@ -733,15 +729,16 @@ class PaymentEntry(AccountsController): "cost_center": d.cost_center }, account_currency, item=d)) + #Intentionally use -1 to get net values in party account gl_entries.append( self.get_gl_dict({ - "account": self.advance_tax_account, + "account": payment_or_advance_account, "against": self.party if self.payment_type=="Receive" else self.paid_from, - rev_dr_cr: d.base_tax_amount, - rev_dr_cr + "_in_account_currency": d.base_tax_amount + dr_or_cr: -1 * d.base_tax_amount, + dr_or_cr + "_in_account_currency": -1*d.base_tax_amount if account_currency==self.company_currency else d.tax_amount, - "cost_center": d.cost_center or self.cost_center + "cost_center": self.cost_center, }, account_currency, item=d)) def add_deductions_gl_entries(self, gl_entries): @@ -762,6 +759,14 @@ class PaymentEntry(AccountsController): }, item=d) ) + def get_party_account_for_taxes(self): + if self.advance_tax_account: + return self.advance_tax_account + elif self.payment_type == 'Pay': + return self.paid_from + elif self.payment_type == 'Receive': + return self.paid_to + def update_advance_paid(self): if self.payment_type in ("Receive", "Pay") and self.party: for d in self.get("references"): @@ -808,32 +813,52 @@ class PaymentEntry(AccountsController): self.append('deductions', row) self.set_unallocated_amount() + def initialize_taxes(self): + for tax in self.get("taxes"): + tax_fields = ["total", "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"] + + if tax.charge_type != "Actual": + tax_fields.append("tax_amount") + + for fieldname in tax_fields: + tax.set(fieldname, 0.0) + + self.paid_amount_after_tax = self.paid_amount + + def determine_exclusive_rate(self): + if not any((cint(tax.included_in_paid_amount) for tax in self.get("taxes"))): + return + + cumulated_tax_fraction = 0 + total_inclusive_tax_amount_per_qty = 0 + for i, tax in enumerate(self.get("taxes")): + + tax.tax_fraction_for_current_item = self.get_current_tax_fraction(tax) + if i==0: + tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item + else: + tax.grand_total_fraction_for_current_item = \ + self.get("taxes")[i-1].grand_total_fraction_for_current_item \ + + tax.tax_fraction_for_current_item + + cumulated_tax_fraction += tax.tax_fraction_for_current_item + + self.paid_amount_after_tax = flt(self.paid_amount/(1+cumulated_tax_fraction)) + def calculate_taxes(self): self.total_taxes_and_charges = 0.0 self.base_total_taxes_and_charges = 0.0 + actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))] + for tax in self.get("taxes") if tax.charge_type == "Actual"]) + for i, tax in enumerate(self.get('taxes')): - tax_rate = tax.rate - - # To set row_id by default as previous row. - if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"]: - if tax.idx == 1: - frappe.throw(_("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")) - - if not tax.row_id: - tax.row_id = tax.idx - 1 + current_tax_amount = self.get_current_tax_amount(tax) if tax.charge_type == "Actual": - current_tax_amount = flt(tax.tax_amount, self.precision("tax_amount", tax)) - elif tax.charge_type == "On Paid Amount": - current_tax_amount = (tax_rate / 100.0) * self.paid_amount - elif tax.charge_type == "On Previous Row Amount": - current_tax_amount = (tax_rate / 100.0) * \ - self.get('taxes')[cint(tax.row_id) - 1].tax_amount - - elif tax.charge_type == "On Previous Row Total": - current_tax_amount = (tax_rate / 100.0) * \ - self.get('taxes')[cint(tax.row_id) - 1].total + actual_tax_dict[tax.idx] -= current_tax_amount + if i == len(self.get("taxes")) - 1: + current_tax_amount += actual_tax_dict[tax.idx] tax.tax_amount = current_tax_amount tax.base_tax_amount = tax.tax_amount * self.source_exchange_rate @@ -843,21 +868,68 @@ class PaymentEntry(AccountsController): else: current_tax_amount *= 1.0 - if not tax.included_in_paid_amount: - applicable_tax = current_tax_amount - else: - applicable_tax = 0 - if i == 0: - tax.total = flt(self.paid_amount + applicable_tax, self.precision("total", tax)) + tax.total = flt(self.paid_amount_after_tax + current_tax_amount, self.precision("total", tax)) else: - tax.total = flt(self.get('taxes')[i-1].total + applicable_tax, self.precision("total", tax)) + tax.total = flt(self.get('taxes')[i-1].total + current_tax_amount, self.precision("total", tax)) tax.base_total = tax.total * self.source_exchange_rate self.total_taxes_and_charges += current_tax_amount self.base_total_taxes_and_charges += current_tax_amount * self.source_exchange_rate + if self.get('taxes'): + self.paid_amount_after_tax = self.get('taxes')[-1].base_total + + def get_current_tax_amount(self, tax): + tax_rate = tax.rate + + # To set row_id by default as previous row. + if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"]: + if tax.idx == 1: + frappe.throw(_("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")) + + if not tax.row_id: + tax.row_id = tax.idx - 1 + + if tax.charge_type == "Actual": + current_tax_amount = flt(tax.tax_amount, self.precision("tax_amount", tax)) + elif tax.charge_type == "On Paid Amount": + current_tax_amount = (tax_rate / 100.0) * self.paid_amount_after_tax + elif tax.charge_type == "On Previous Row Amount": + current_tax_amount = (tax_rate / 100.0) * \ + self.get('taxes')[cint(tax.row_id) - 1].tax_amount + + elif tax.charge_type == "On Previous Row Total": + current_tax_amount = (tax_rate / 100.0) * \ + self.get('taxes')[cint(tax.row_id) - 1].total + + return current_tax_amount + + def get_current_tax_fraction(self, tax): + current_tax_fraction = 0 + + if cint(tax.included_in_paid_amount): + tax_rate = tax.rate + + if tax.charge_type == 'Actual': + current_tax_fraction = tax.tax_amount/self.paid_amount_after_tax + elif tax.charge_type == "On Paid Amount": + current_tax_fraction = tax_rate / 100.0 + + elif tax.charge_type == "On Previous Row Amount": + current_tax_fraction = (tax_rate / 100.0) * \ + self.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item + + elif tax.charge_type == "On Previous Row Total": + current_tax_fraction = (tax_rate / 100.0) * \ + self.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item + + if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct": + current_tax_fraction *= -1.0 + + return current_tax_fraction + @frappe.whitelist() def get_outstanding_reference_documents(args): @@ -1426,6 +1498,9 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= pe.apply_tax_withholding_amount = 1 pe.tax_withholding_category = doc.tax_withholding_category + if not pe.advance_tax_account: + pe.advance_tax_account = frappe.db.get_value('Company', pe.company, 'unrealized_profit_loss_account') + return pe def get_bank_cash_account(doc, bank_account): From bbf07d9214bf86eb120a7ad14989b2bd21a5d9d7 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 8 Jun 2021 17:05:44 +0530 Subject: [PATCH 102/180] fix: quiz timer issues --- erpnext/education/utils.py | 13 +++++++------ erpnext/public/js/education/lms/quiz.js | 10 ++++------ erpnext/www/lms/content.html | 1 + 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/education/utils.py b/erpnext/education/utils.py index 8f51fef847..9d5653c170 100644 --- a/erpnext/education/utils.py +++ b/erpnext/education/utils.py @@ -219,7 +219,6 @@ def get_quiz(quiz_name, course): try: quiz = frappe.get_doc("Quiz", quiz_name) questions = quiz.get_questions() - duration = quiz.duration except: frappe.throw(_("Quiz {0} does not exist").format(quiz_name), frappe.DoesNotExistError) return None @@ -236,15 +235,17 @@ def get_quiz(quiz_name, course): return { 'questions': questions, 'activity': None, - 'duration':duration + 'is_time_bound': quiz.is_time_bound, + 'duration': quiz.duration } student = get_current_student() course_enrollment = get_enrollment("course", course, student.name) status, score, result, time_taken = check_quiz_completion(quiz, course_enrollment) return { - 'questions': questions, + 'questions': questions, 'activity': {'is_complete': status, 'score': score, 'result': result, 'time_taken': time_taken}, + 'is_time_bound': quiz.is_time_bound, 'duration': quiz.duration } @@ -372,9 +373,9 @@ def check_content_completion(content_name, content_type, enrollment_name): def check_quiz_completion(quiz, enrollment_name): attempts = frappe.get_all("Quiz Activity", filters={ - 'enrollment': enrollment_name, + 'enrollment': enrollment_name, 'quiz': quiz.name - }, + }, fields=["name", "activity_date", "score", "status", "time_taken"] ) status = False if quiz.max_attempts == 0 else bool(len(attempts) >= quiz.max_attempts) @@ -389,4 +390,4 @@ def check_quiz_completion(quiz, enrollment_name): time_taken = attempts[0]['time_taken'] if result == 'Pass': status = True - return status, score, result, time_taken \ No newline at end of file + return status, score, result, time_taken diff --git a/erpnext/public/js/education/lms/quiz.js b/erpnext/public/js/education/lms/quiz.js index 32fa4ab1ec..5bb8425c85 100644 --- a/erpnext/public/js/education/lms/quiz.js +++ b/erpnext/public/js/education/lms/quiz.js @@ -20,10 +20,8 @@ class Quiz { } make(data) { - if (data.duration) { - const timer_display = document.createElement("div"); - timer_display.classList.add("lms-timer", "float-right", "font-weight-bold"); - document.getElementsByClassName("lms-title")[0].appendChild(timer_display); + if (data.is_time_bound) { + $(".lms-timer").removeClass("hide") if (!data.activity || (data.activity && !data.activity.is_complete)) { this.initialiseTimer(data.duration); this.is_time_bound = true; @@ -118,7 +116,7 @@ class Quiz { quiz_response: this.get_selected(), course: this.course, program: this.program, - time_taken: this.is_time_bound ? this.time_taken : "" + time_taken: this.is_time_bound ? this.time_taken : 0 }).then(res => { this.submit_btn.remove() if (!res.message) { @@ -237,4 +235,4 @@ class Question { this.options = option_list this.wrapper.appendChild(options_wrapper) } -} \ No newline at end of file +} diff --git a/erpnext/www/lms/content.html b/erpnext/www/lms/content.html index 15afb097b9..d22ef66d2a 100644 --- a/erpnext/www/lms/content.html +++ b/erpnext/www/lms/content.html @@ -64,6 +64,7 @@

{{ content.name }} ({{ position + 1 }}/{{length}})

+
{% endmacro %} From 0ea4d850e1c3870c750072e0ce89a6cd73605266 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Tue, 8 Jun 2021 17:23:44 +0530 Subject: [PATCH 103/180] fix: Allow all System Managers to delete company transactions (#25834) --- .../transaction_deletion_record.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 38f8de7a66..ece9fb5699 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -12,10 +12,6 @@ from frappe.desk.notifications import clear_notifications class TransactionDeletionRecord(Document): def validate(self): frappe.only_for('System Manager') - company_obj = frappe.get_doc('Company', self.company) - if frappe.session.user != company_obj.owner and frappe.session.user != 'Administrator': - frappe.throw(_('Transactions can only be deleted by the creator of the Company or the Administrator.'), - frappe.PermissionError) doctypes_to_be_ignored_list = get_doctypes_to_be_ignored() for doctype in self.doctypes_to_be_ignored: if doctype.doctype_name not in doctypes_to_be_ignored_list: From 7ace06ac21c7d29d8470df139979c15f46a82ed1 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 8 Jun 2021 18:26:23 +0530 Subject: [PATCH 104/180] fix: sider --- erpnext/education/utils.py | 2 +- erpnext/public/js/education/lms/quiz.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/education/utils.py b/erpnext/education/utils.py index 9d5653c170..9db8a4a90d 100644 --- a/erpnext/education/utils.py +++ b/erpnext/education/utils.py @@ -245,7 +245,7 @@ def get_quiz(quiz_name, course): return { 'questions': questions, 'activity': {'is_complete': status, 'score': score, 'result': result, 'time_taken': time_taken}, - 'is_time_bound': quiz.is_time_bound, + 'is_time_bound': quiz.is_time_bound, 'duration': quiz.duration } diff --git a/erpnext/public/js/education/lms/quiz.js b/erpnext/public/js/education/lms/quiz.js index 5bb8425c85..66160a7610 100644 --- a/erpnext/public/js/education/lms/quiz.js +++ b/erpnext/public/js/education/lms/quiz.js @@ -21,7 +21,7 @@ class Quiz { make(data) { if (data.is_time_bound) { - $(".lms-timer").removeClass("hide") + $(".lms-timer").removeClass("hide"); if (!data.activity || (data.activity && !data.activity.is_complete)) { this.initialiseTimer(data.duration); this.is_time_bound = true; From 18eed58fc5afa1ef95037a1c297b308c34de58ee Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 8 Jun 2021 20:52:14 +0530 Subject: [PATCH 105/180] fix(e-invoicing): service item check --- 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 843fb012b9..a1179ff9b6 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -199,7 +199,7 @@ def get_item_list(invoice): 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 - item.is_service_item = 'N' if frappe.db.get_value('Item', d.item_code, 'is_stock_item') else 'Y' + item.is_service_item = 'Y' if item.gst_hsn_code[:2] == "99" else 'N' item.serial_no = "" item = update_item_taxes(invoice, item) From 99b583f688966cc89d069f8c5b7c4cf88108abb5 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 9 Jun 2021 00:06:35 +0530 Subject: [PATCH 106/180] fix: on click of duplicate button system copy the difference account from first row --- erpnext/stock/doctype/stock_entry/stock_entry.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 93a6fc0e0a..1a25994b24 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -986,7 +986,10 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ items_add: function(doc, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); - this.frm.script_manager.copy_from_first_row("items", row, ["expense_account", "cost_center"]); + + if (!(row.expense_account && row.cost_center)) { + this.frm.script_manager.copy_from_first_row("items", row, ["expense_account", "cost_center"]); + } if(!row.s_warehouse) row.s_warehouse = this.frm.doc.from_warehouse; if(!row.t_warehouse) row.t_warehouse = this.frm.doc.to_warehouse; From 21b8e2f0d8f18972b05783eface42404cc104c30 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 9 Jun 2021 12:57:21 +0530 Subject: [PATCH 107/180] fix: hiding Rounding Adjustment field (#25380) * fix: hiding Rounding Adjustment field * fix: updating purchase_invoice.json --- .../accounts/doctype/purchase_invoice/purchase_invoice.json | 4 +++- erpnext/buying/doctype/purchase_order/purchase_order.json | 4 +++- .../buying/doctype/supplier_quotation/supplier_quotation.json | 4 +++- erpnext/stock/doctype/purchase_receipt/purchase_receipt.json | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index d3d3ffa17f..9157821520 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -837,6 +837,7 @@ "read_only": 1 }, { + "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "base_rounding_adjustment", "fieldtype": "Currency", "label": "Rounding Adjustment (Company Currency)", @@ -883,6 +884,7 @@ "read_only": 1 }, { + "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "rounding_adjustment", "fieldtype": "Currency", "label": "Rounding Adjustment", @@ -1380,7 +1382,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2021-04-30 22:45:58.334107", + "modified": "2021-06-09 12:30:25.632109", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index ee2beea67f..8677c71bc5 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -765,6 +765,7 @@ "read_only": 1 }, { + "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "base_rounding_adjustment", "fieldtype": "Currency", "label": "Rounding Adjustment (Company Currency)", @@ -810,6 +811,7 @@ "read_only": 1 }, { + "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "rounding_adjustment", "fieldtype": "Currency", "label": "Rounding Adjustment", @@ -1124,7 +1126,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2021-01-20 22:07:23.487138", + "modified": "2021-04-19 00:55:30.781375", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 40fbe2c26e..0a51a8e9a1 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -576,6 +576,7 @@ "read_only": 1 }, { + "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "base_rounding_adjustment", "fieldtype": "Currency", "label": "Rounding Adjustment (Company Currency", @@ -620,6 +621,7 @@ "read_only": 1 }, { + "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "rounding_adjustment", "fieldtype": "Currency", "label": "Rounding Adjustment", @@ -802,7 +804,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-12-03 15:18:29.073368", + "modified": "2021-04-19 00:58:20.995491", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 32d349f303..ad350d344f 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -762,6 +762,7 @@ "read_only": 1 }, { + "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "base_rounding_adjustment", "fieldtype": "Currency", "label": "Rounding Adjustment (Company Currency)", @@ -805,6 +806,7 @@ "read_only": 1 }, { + "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "rounding_adjustment", "fieldtype": "Currency", "label": "Rounding Adjustment", @@ -1147,7 +1149,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-12-26 20:49:39.106049", + "modified": "2021-04-19 01:01:00.754119", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", From 8090965162fc314ff8f41d71e5890f8c7f060802 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 9 Jun 2021 13:45:42 +0530 Subject: [PATCH 108/180] fix: Debug test --- .../accounts/doctype/purchase_invoice/test_purchase_invoice.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 8afb9a29f4..5187ca1b5e 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1017,6 +1017,9 @@ class TestPurchaseInvoice(unittest.TestCase): where voucher_type='Purchase Invoice' and voucher_no=%s order by account asc""", (purchase_invoice.name), as_dict=1) + for i, gle in enumerate(gl_entries): + print(gle.account, gle.debit, gle.credit) + for i, gle in enumerate(gl_entries): self.assertEqual(expected_gle[i][0], gle.account) self.assertEqual(expected_gle[i][1], gle.debit) From 4c1dad820780b88ca7c5710e570003974c11bd54 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 9 Jun 2021 14:18:23 +0530 Subject: [PATCH 109/180] fix: Debug tests --- .../accounts/doctype/purchase_invoice/test_purchase_invoice.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 5187ca1b5e..3299ecc5bc 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -991,6 +991,9 @@ class TestPurchaseInvoice(unittest.TestCase): where voucher_type='Payment Entry' and voucher_no=%s order by account asc""", (payment_entry.name), as_dict=1) + for i, gle in enumerate(gl_entries): + print(gle.account, gle.debit, gle.credit) + for i, gle in enumerate(gl_entries): self.assertEqual(expected_gle[i][0], gle.account) self.assertEqual(expected_gle[i][1], gle.debit) From b57ebba3fd8af3cb70b16a340d41961ba6eb7223 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 9 Jun 2021 17:56:41 +0530 Subject: [PATCH 110/180] fix: Validate negative allocated amount in Payment Entry (#25799) --- .../invoice_discounting/invoice_discounting.py | 7 +++++-- .../doctype/payment_entry/payment_entry.py | 14 ++++++++++---- .../doctype/payment_request/payment_request.py | 3 +-- .../accounts_receivable/accounts_receivable.py | 1 + 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py index 7b62b617f9..95d2ee56d9 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py @@ -49,11 +49,11 @@ class InvoiceDiscounting(AccountsController): self.make_gl_entries() def on_cancel(self): - self.set_status() + self.set_status(cancel=1) self.update_sales_invoice() self.make_gl_entries() - def set_status(self, status=None): + def set_status(self, status=None, cancel=0): if status: self.status = status self.db_set("status", status) @@ -66,6 +66,9 @@ class InvoiceDiscounting(AccountsController): elif self.docstatus == 2: self.status = "Cancelled" + if cancel: + self.db_set('status', self.status, update_modified = True) + def update_sales_invoice(self): for d in self.invoices: if self.docstatus == 1: diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 62ab76c323..e01c651a93 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -65,7 +65,6 @@ class PaymentEntry(AccountsController): self.set_status() def on_submit(self): - self.setup_party_account_field() if self.difference_amount: frappe.throw(_("Difference Amount must be zero")) self.make_gl_entries() @@ -78,7 +77,6 @@ class PaymentEntry(AccountsController): def on_cancel(self): self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') - self.setup_party_account_field() self.make_gl_entries(cancel=1) self.update_outstanding_amounts() self.update_advance_paid() @@ -122,6 +120,11 @@ class PaymentEntry(AccountsController): if flt(d.allocated_amount) > flt(d.outstanding_amount): frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx)) + # Check for negative outstanding invoices as well + if flt(d.allocated_amount) < 0: + if flt(d.allocated_amount) < flt(d.outstanding_amount): + frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx)) + def delink_advance_entry_references(self): for reference in self.references: if reference.reference_doctype in ("Sales Invoice", "Purchase Invoice"): @@ -177,7 +180,7 @@ class PaymentEntry(AccountsController): for field, value in iteritems(ref_details): if field == 'exchange_rate' or not d.get(field) or force: - d.set(field, value) + d.db_set(field, value) def validate_payment_type(self): if self.payment_type not in ("Receive", "Pay", "Internal Transfer"): @@ -386,6 +389,8 @@ class PaymentEntry(AccountsController): else: self.status = 'Draft' + self.db_set('status', self.status, update_modified = True) + def set_amounts(self): self.set_amounts_in_company_currency() self.set_total_allocated_amount() @@ -791,7 +796,7 @@ def split_invoices_based_on_payment_terms(outstanding_invoices): outstanding_invoices.pop(idx - 1) outstanding_invoices += invoice_ref_based_on_payment_terms[idx] - + return outstanding_invoices def get_orders_to_be_billed(posting_date, party_type, party, @@ -989,6 +994,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre outstanding_amount = ref_doc.get("outstanding_amount") elif reference_doctype == "Donation": total_amount = ref_doc.get("amount") + outstanding_amount = total_amount exchange_rate = 1 elif reference_doctype == "Dunning": total_amount = ref_doc.get("dunning_amount") diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 53ac996290..468978785b 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -101,7 +101,7 @@ class PaymentRequest(Document): 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, @@ -492,7 +492,6 @@ def update_payment_req_status(doc, method): status = 'Requested' pay_req_doc.db_set('status', status) - frappe.db.commit() def get_dummy_message(doc): return frappe.render_template("""{% if doc.contact_person -%} diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index db605f7285..a11b77a6f6 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -584,6 +584,7 @@ class ReceivablePayableReport(object): `tabGL Entry` where docstatus < 2 + and is_cancelled = 0 and party_type=%s and (party is not null and party != '') {1} {2} {3}""" From 9e648183dbc52ee526b3a3dd15b1f57110ebccb5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 9 Jun 2021 18:26:16 +0530 Subject: [PATCH 111/180] fix: Test case --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 -- .../doctype/purchase_invoice/test_purchase_invoice.py | 8 +------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 5796fc0800..d04d6a0d15 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -835,9 +835,7 @@ class PaymentEntry(AccountsController): return cumulated_tax_fraction = 0 - total_inclusive_tax_amount_per_qty = 0 for i, tax in enumerate(self.get("taxes")): - tax.tax_fraction_for_current_item = self.get_current_tax_fraction(tax) if i==0: tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 3299ecc5bc..8e72dc6321 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -991,9 +991,6 @@ class TestPurchaseInvoice(unittest.TestCase): where voucher_type='Payment Entry' and voucher_no=%s order by account asc""", (payment_entry.name), as_dict=1) - for i, gle in enumerate(gl_entries): - print(gle.account, gle.debit, gle.credit) - for i, gle in enumerate(gl_entries): self.assertEqual(expected_gle[i][0], gle.account) self.assertEqual(expected_gle[i][1], gle.debit) @@ -1011,8 +1008,8 @@ class TestPurchaseInvoice(unittest.TestCase): ['_Test Account Cost for Goods Sold - _TC', 30000, 0], ['_Test Account Excise Duty - _TC', 0, 3000], ['Creditors - _TC', 0, 27000], - ['TDS Payable - _TC', 0, 3000], ['TDS Payable - _TC', 3000, 0] + ['TDS Payable - _TC', 0, 3000], ] gl_entries = frappe.db.sql("""select account, debit, credit @@ -1020,9 +1017,6 @@ class TestPurchaseInvoice(unittest.TestCase): where voucher_type='Purchase Invoice' and voucher_no=%s order by account asc""", (purchase_invoice.name), as_dict=1) - for i, gle in enumerate(gl_entries): - print(gle.account, gle.debit, gle.credit) - for i, gle in enumerate(gl_entries): self.assertEqual(expected_gle[i][0], gle.account) self.assertEqual(expected_gle[i][1], gle.debit) From 26f06093903036bdb222e523d3dba12888869086 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 9 Jun 2021 19:47:28 +0530 Subject: [PATCH 112/180] feat: enable/disable gl entry posting for change given in pos (#25822) --- .../accounts_settings/accounts_settings.json | 9 +++- .../doctype/sales_invoice/sales_invoice.py | 12 ++++- .../sales_invoice/test_sales_invoice.py | 44 +++++++++++++++++-- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 781f94e203..2735b1ccee 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -18,6 +18,7 @@ "delete_linked_ledger_entries", "book_asset_depreciation_entry_automatically", "unlink_advance_payment_on_cancelation_of_order", + "post_change_gl_entries", "tax_settings_section", "determine_address_tax_category_from", "column_break_19", @@ -253,6 +254,12 @@ { "fieldname": "column_break_19", "fieldtype": "Column Break" + }, + { + "default": "1", + "fieldname": "post_change_gl_entries", + "fieldtype": "Check", + "label": "Post Ledger Entries for Given Change" } ], "icon": "icon-cog", @@ -260,7 +267,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-04-30 15:25:10.381008", + "modified": "2021-05-25 12:34:05.858669", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index f8b5179d2c..0b8d28aef0 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -849,7 +849,6 @@ class SalesInvoice(SellingController): self.make_loyalty_point_redemption_gle(gl_entries) self.make_pos_gl_entries(gl_entries) - self.make_gle_for_change_amount(gl_entries) self.make_write_off_gl_entry(gl_entries) self.make_gle_for_rounding_adjustment(gl_entries) @@ -983,7 +982,13 @@ class SalesInvoice(SellingController): def make_pos_gl_entries(self, gl_entries): if cint(self.is_pos): + + skip_change_gl_entries = not cint(frappe.db.get_single_value('Accounts Settings', 'post_change_gl_entries')) + for payment_mode in self.payments: + if skip_change_gl_entries and payment_mode.account == self.account_for_change_amount: + payment_mode.base_amount -= self.change_amount + if payment_mode.amount: # POS, make payment entries gl_entries.append( @@ -1015,8 +1020,11 @@ class SalesInvoice(SellingController): }, payment_mode_account_currency, item=self) ) + if not skip_change_gl_entries: + self.make_gle_for_change_amount(gl_entries) + def make_gle_for_change_amount(self, gl_entries): - if cint(self.is_pos) and self.change_amount: + if self.change_amount: if self.account_for_change_amount: gl_entries.append( self.get_gl_dict({ diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index df6d483904..5409a6f192 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -713,7 +713,7 @@ class TestSalesInvoice(unittest.TestCase): si.submit() self.assertEqual(si.paid_amount, 100.0) - self.pos_gl_entry(si, pos, 50) + self.validate_pos_gl_entry(si, pos, 50) def test_pos_returns_with_repayment(self): from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return @@ -749,7 +749,7 @@ class TestSalesInvoice(unittest.TestCase): 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", + make_purchase_receipt(company= "_Test Company with perpetual inventory", item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1") pos = create_sales_invoice(company= "_Test Company with perpetual inventory", @@ -770,7 +770,45 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(pos.grand_total, 100.0) self.assertEqual(pos.write_off_amount, -5) - def pos_gl_entry(self, si, pos, cash_amount): + def test_pos_with_no_gl_entry_for_change_amount(self): + frappe.db.set_value('Accounts Settings', None, 'post_change_gl_entries', 0) + + 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") + + make_purchase_receipt(company= "_Test Company with perpetual inventory", + item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1") + + pos = create_sales_invoice(company= "_Test Company with perpetual inventory", + debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", + income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", + cost_center = "Main - TCP1", do_not_save=True) + + pos.is_pos = 1 + pos.update_stock = 1 + + taxes = get_taxes_and_charges() + pos.taxes = [] + for tax in taxes: + pos.append("taxes", tax) + + pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 50}) + pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - TCP1', 'amount': 60}) + + pos.insert() + pos.submit() + + self.assertEqual(pos.grand_total, 100.0) + self.assertEqual(pos.change_amount, 10) + + self.validate_pos_gl_entry(pos, pos, 60, validate_without_change_gle=True) + + frappe.db.set_value('Accounts Settings', None, 'post_change_gl_entries', 1) + + def validate_pos_gl_entry(self, si, pos, cash_amount, validate_without_change_gle=False): + if validate_without_change_gle: + cash_amount -= pos.change_amount + # check stock ledger entries sle = frappe.db.sql("""select * from `tabStock Ledger Entry` where voucher_type = 'Sales Invoice' and voucher_no = %s""", From 42557c4ad387db7ea4956ac80dfaea085614481d Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 9 Jun 2021 19:48:31 +0530 Subject: [PATCH 113/180] feat: cost-center wise period closing entry (#25766) --- .../period_closing_voucher.json | 452 +++++------------- .../period_closing_voucher.py | 111 +++-- .../test_period_closing_voucher.py | 85 ++++ 3 files changed, 277 insertions(+), 371 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json index 47546c07a4..84c941ecc1 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json @@ -1,350 +1,138 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "ACC-PCV-.YYYY.-.#####", - "beta": 0, - "creation": "2013-01-10 16:34:07", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 0, - "engine": "InnoDB", + "actions": [], + "autoname": "ACC-PCV-.YYYY.-.#####", + "creation": "2013-01-10 16:34:07", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "transaction_date", + "posting_date", + "fiscal_year", + "amended_from", + "company", + "cost_center_wise_pnl", + "column_break1", + "closing_account_head", + "remarks" + ], "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, - "oldfieldname": "transaction_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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "transaction_date", + "fieldtype": "Date", + "label": "Transaction Date", + "oldfieldname": "transaction_date", + "oldfieldtype": "Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "posting_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": "Posting Date", - "length": 0, - "no_copy": 0, - "oldfieldname": "posting_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": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "oldfieldname": "posting_date", + "oldfieldtype": "Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "fiscal_year", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Closing Fiscal Year", - "length": 0, - "no_copy": 0, - "oldfieldname": "fiscal_year", - "oldfieldtype": "Select", - "options": "Fiscal Year", - "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": "fiscal_year", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Closing Fiscal Year", + "oldfieldname": "fiscal_year", + "oldfieldtype": "Select", + "options": "Fiscal Year", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "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, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Period Closing Voucher", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Period Closing Voucher", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "oldfieldname": "company", - "oldfieldtype": "Select", - "options": "Company", - "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": "company", + "fieldtype": "Link", + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Select", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break1", - "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, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_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_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "The account head under Liability or Equity, in which Profit/Loss will be booked", - "fieldname": "closing_account_head", - "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": "Closing Account Head", - "length": 0, - "no_copy": 0, - "oldfieldname": "closing_account_head", - "oldfieldtype": "Link", - "options": "Account", - "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": "The account head under Liability or Equity, in which Profit/Loss will be booked", + "fieldname": "closing_account_head", + "fieldtype": "Link", + "label": "Closing Account Head", + "oldfieldname": "closing_account_head", + "oldfieldtype": "Link", + "options": "Account", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "remarks", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Remarks", - "length": 0, - "no_copy": 0, - "oldfieldname": "remarks", - "oldfieldtype": "Small Text", - "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": "remarks", + "fieldtype": "Small Text", + "label": "Remarks", + "oldfieldname": "remarks", + "oldfieldtype": "Small Text", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "cost_center_wise_pnl", + "fieldtype": "Check", + "label": "Book Cost Center Wise Profit/Loss" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-file-text", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Period Closing Voucher", - "owner": "Administrator", + ], + "icon": "fa fa-file-text", + "idx": 1, + "is_submittable": 1, + "links": [], + "modified": "2021-05-20 15:27:37.210458", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Period Closing Voucher", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "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": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "posting_date, fiscal_year", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "closing_account_head", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "search_fields": "posting_date, fiscal_year", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "closing_account_head" } \ No newline at end of file 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 a74fa062b6..b0a5b04de6 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -51,63 +51,96 @@ class PeriodClosingVoucher(AccountsController): def make_gl_entries(self): gl_entries = [] - net_pl_balance = 0 - dimension_fields = ['t1.cost_center'] + net_pl_balance = 0 - accounting_dimensions = get_accounting_dimensions() - for dimension in accounting_dimensions: - dimension_fields.append('t1.{0}'.format(dimension)) - - dimension_filters, default_dimensions = get_dimensions() - - pl_accounts = self.get_pl_balances(dimension_fields) + pl_accounts = self.get_pl_balances() for acc in pl_accounts: - if flt(acc.balance_in_company_currency): + if flt(acc.bal_in_company_currency): gl_entries.append(self.get_gl_dict({ "account": acc.account, "cost_center": acc.cost_center, "account_currency": acc.account_currency, - "debit_in_account_currency": abs(flt(acc.balance_in_account_currency)) \ - if flt(acc.balance_in_account_currency) < 0 else 0, - "debit": abs(flt(acc.balance_in_company_currency)) \ - if flt(acc.balance_in_company_currency) < 0 else 0, - "credit_in_account_currency": abs(flt(acc.balance_in_account_currency)) \ - if flt(acc.balance_in_account_currency) > 0 else 0, - "credit": abs(flt(acc.balance_in_company_currency)) \ - if flt(acc.balance_in_company_currency) > 0 else 0 + "debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) < 0 else 0, + "debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0, + "credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0, + "credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0 }, item=acc)) - net_pl_balance += flt(acc.balance_in_company_currency) + net_pl_balance += flt(acc.bal_in_company_currency) if net_pl_balance: - cost_center = frappe.db.get_value("Company", self.company, "cost_center") - gl_entry = self.get_gl_dict({ - "account": self.closing_account_head, - "debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0, - "debit": abs(net_pl_balance) if net_pl_balance > 0 else 0, - "credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0, - "credit": abs(net_pl_balance) if net_pl_balance < 0 else 0, - "cost_center": cost_center - }) - - for dimension in accounting_dimensions: - gl_entry.update({ - dimension: default_dimensions.get(self.company, {}).get(dimension) - }) - - gl_entries.append(gl_entry) + if self.cost_center_wise_pnl: + costcenter_wise_gl_entries = self.get_costcenter_wise_pnl_gl_entries(pl_accounts) + gl_entries += costcenter_wise_gl_entries + else: + gl_entry = self.get_pnl_gl_entry(net_pl_balance) + gl_entries.append(gl_entry) from erpnext.accounts.general_ledger import make_gl_entries make_gl_entries(gl_entries) + + def get_pnl_gl_entry(self, net_pl_balance): + cost_center = frappe.db.get_value("Company", self.company, "cost_center") + gl_entry = self.get_gl_dict({ + "account": self.closing_account_head, + "debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0, + "debit": abs(net_pl_balance) if net_pl_balance > 0 else 0, + "credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0, + "credit": abs(net_pl_balance) if net_pl_balance < 0 else 0, + "cost_center": cost_center + }) + + self.update_default_dimensions(gl_entry) + + return gl_entry + + def get_costcenter_wise_pnl_gl_entries(self, pl_accounts): + company_cost_center = frappe.db.get_value("Company", self.company, "cost_center") + gl_entries = [] + + for acc in pl_accounts: + if flt(acc.bal_in_company_currency): + gl_entry = self.get_gl_dict({ + "account": self.closing_account_head, + "cost_center": acc.cost_center or company_cost_center, + "account_currency": acc.account_currency, + "debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0, + "debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0, + "credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) < 0 else 0, + "credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0 + }, item=acc) + + self.update_default_dimensions(gl_entry) + + gl_entries.append(gl_entry) + + return gl_entries + + def update_default_dimensions(self, gl_entry): + if not self.accounting_dimensions: + self.accounting_dimensions = get_accounting_dimensions() + + _, default_dimensions = get_dimensions() + for dimension in self.accounting_dimensions: + gl_entry.update({ + dimension: default_dimensions.get(self.company, {}).get(dimension) + }) + + def get_pl_balances(self): + """Get balance for dimension-wise pl accounts""" + + dimension_fields = ['t1.cost_center'] + + self.accounting_dimensions = get_accounting_dimensions() + for dimension in self.accounting_dimensions: + dimension_fields.append('t1.{0}'.format(dimension)) - def get_pl_balances(self, dimension_fields): - """Get balance for pl accounts""" return frappe.db.sql(""" select t1.account, t2.account_currency, {dimension_fields}, - sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as balance_in_account_currency, - sum(t1.debit) - sum(t1.credit) as balance_in_company_currency + sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as bal_in_account_currency, + sum(t1.debit) - sum(t1.credit) as bal_in_company_currency from `tabGL Entry` t1, `tabAccount` t2 where t1.account = t2.name and t2.report_type = 'Profit and Loss' and t2.docstatus < 2 and t2.company = %s diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py index eb02d97b78..2f29372b01 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py @@ -8,6 +8,7 @@ import frappe from frappe.utils import flt, today from erpnext.accounts.utils import get_fiscal_year, now from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice class TestPeriodClosingVoucher(unittest.TestCase): def test_closing_entry(self): @@ -65,6 +66,58 @@ class TestPeriodClosingVoucher(unittest.TestCase): self.assertEqual(gle_for_random_expense_account[0].amount_in_account_currency, -1*random_expense_account[0].balance_in_account_currency) + def test_cost_center_wise_posting(self): + frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'") + + company = create_company() + surplus_account = create_account() + + cost_center1 = create_cost_center("Test Cost Center 1") + cost_center2 = create_cost_center("Test Cost Center 2") + + create_sales_invoice( + company=company, + cost_center=cost_center1, + income_account="Sales - TPC", + expense_account="Cost of Goods Sold - TPC", + rate=400, + debit_to="Debtors - TPC" + ) + create_sales_invoice( + company=company, + cost_center=cost_center2, + income_account="Sales - TPC", + expense_account="Cost of Goods Sold - TPC", + rate=200, + debit_to="Debtors - TPC" + ) + + pcv = frappe.get_doc({ + "transaction_date": today(), + "posting_date": today(), + "fiscal_year": get_fiscal_year(today())[0], + "company": "Test PCV Company", + "cost_center_wise_pnl": 1, + "closing_account_head": surplus_account, + "remarks": "Test", + "doctype": "Period Closing Voucher" + }) + pcv.insert() + pcv.submit() + + expected_gle = ( + ('Sales - TPC', 200.0, 0.0, cost_center2), + (surplus_account, 0.0, 200.0, cost_center2), + ('Sales - TPC', 400.0, 0.0, cost_center1), + (surplus_account, 0.0, 400.0, cost_center1) + ) + + pcv_gle = frappe.db.sql(""" + select account, debit, credit, cost_center from `tabGL Entry` where voucher_no=%s + """, (pcv.name)) + + self.assertTrue(pcv_gle, expected_gle) + def make_period_closing_voucher(self): pcv = frappe.get_doc({ "doctype": "Period Closing Voucher", @@ -80,6 +133,38 @@ class TestPeriodClosingVoucher(unittest.TestCase): return pcv +def create_company(): + company = frappe.get_doc({ + 'doctype': 'Company', + 'company_name': "Test PCV Company", + 'country': 'United States', + 'default_currency': 'USD' + }) + company.insert(ignore_if_duplicate = True) + return company.name + +def create_account(): + account = frappe.get_doc({ + "account_name": "Reserve and Surplus", + "is_group": 0, + "company": "Test PCV Company", + "root_type": "Liability", + "report_type": "Balance Sheet", + "account_currency": "USD", + "parent_account": "Current Liabilities - TPC", + "doctype": "Account" + }).insert(ignore_if_duplicate = True) + return account.name + +def create_cost_center(cc_name): + costcenter = frappe.get_doc({ + "company": "Test PCV Company", + "cost_center_name": cc_name, + "doctype": "Cost Center", + "parent_cost_center": "Test PCV Company - TPC" + }) + costcenter.insert(ignore_if_duplicate = True) + return costcenter.name test_dependencies = ["Customer", "Cost Center"] test_records = frappe.get_test_records("Period Closing Voucher") From 911818a9e2101c63f7bdacc06a07f1c511ba1578 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 9 Jun 2021 22:55:10 +0530 Subject: [PATCH 114/180] fix: Add multiple fixes --- .../advance_taxes_and_charges.json | 4 ++-- erpnext/accounts/doctype/payment_entry/payment_entry.js | 2 +- erpnext/accounts/doctype/payment_entry/payment_entry.json | 7 +++++-- erpnext/accounts/doctype/payment_entry/payment_entry.py | 5 ++++- .../accounts/doctype/purchase_invoice/purchase_invoice.py | 3 --- .../doctype/purchase_invoice/test_purchase_invoice.py | 1 + .../purchase_taxes_and_charges.json | 4 ++-- .../sales_taxes_and_charges/sales_taxes_and_charges.json | 4 ++-- erpnext/controllers/accounts_controller.py | 5 ++++- 9 files changed, 21 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json index 3e4e679c91..4d63499431 100644 --- a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json +++ b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json @@ -160,7 +160,7 @@ "default": "0", "fieldname": "included_in_paid_amount", "fieldtype": "Check", - "label": "Included In Paid Amount" + "label": "Considered In Paid Amount" }, { "fieldname": "allocated_amount", @@ -186,7 +186,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-06-08 07:04:44.287002", + "modified": "2021-06-09 11:46:58.373170", "modified_by": "Administrator", "module": "Accounts", "name": "Advance Taxes and Charges", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 6a3b717b12..939f3546ff 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -1133,7 +1133,7 @@ frappe.ui.form.on('Payment Entry', { let tax_rate = tax.rate; if (tax.charge_type == "Actual") { - current_tax_fraction = tax.tax_amount/frm.doc.paid_amount_after_tax; + current_tax_fraction = tax.tax_amount/(frm.doc.paid_amount_after_tax + frm.doc.tax_amount); } else if(tax.charge_type == "On Paid Amount") { current_tax_fraction = (tax_rate / 100.0); } else if(tax.charge_type == "On Previous Row Amount") { diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 0b4178a547..54623dd6cd 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -681,13 +681,16 @@ "hide_border": 1 }, { + "depends_on": "eval:doc.apply_tax_withholding_amount", + "description": "Provisional tax account for advance tax. Taxes are parked in this account until payments are allocated to invoices", "fieldname": "advance_tax_account", "fieldtype": "Link", "label": "Advance Tax Account", - "mandatory_depends_on": "doc.base_total_taxes_and_charges", + "mandatory_depends_on": "eval:doc.apply_tax_withholding_amount", "options": "Account" }, { + "depends_on": "eval:doc.received_amount", "fieldname": "received_amount_after_tax", "fieldtype": "Currency", "label": "Received Amount After Tax", @@ -704,7 +707,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-05-31 01:54:18.378910", + "modified": "2021-06-09 11:55:04.215050", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index d04d6a0d15..edca210142 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -400,6 +400,9 @@ class PaymentEntry(AccountsController): if not self.apply_tax_withholding_amount: return + if not self.advance_tax_account: + frappe.throw(_("Advance TDS account is mandatory for advance TDS deduction")) + reference_doclist = [] net_total = self.paid_amount included_in_paid_amount = 0 @@ -916,7 +919,7 @@ class PaymentEntry(AccountsController): tax_rate = tax.rate if tax.charge_type == 'Actual': - current_tax_fraction = tax.tax_amount/self.paid_amount_after_tax + current_tax_fraction = tax.tax_amount/ (self.paid_amount_after_tax + tax.tax_amount) elif tax.charge_type == "On Paid Amount": current_tax_fraction = tax_rate / 100.0 diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 934c731cf1..0ee0bc7e11 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -68,9 +68,6 @@ class PurchaseInvoice(BuyingController): super(PurchaseInvoice, self).validate() - # apply tax withholding only if checked and applicable - self.set_tax_withholding() - if not self.is_return: self.po_required() self.pr_required() diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 8e72dc6321..fb4b8d4c1f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -975,6 +975,7 @@ class TestPurchaseInvoice(unittest.TestCase): # Create Payment Entry Against the order payment_entry = get_payment_entry(dt='Purchase Order', dn=po.name) + payment_entry.paid_from = 'Cash - _TC' payment_entry.save() payment_entry.submit() diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json index f6703315fa..9b07645ccc 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json @@ -221,13 +221,13 @@ "description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry", "fieldname": "included_in_paid_amount", "fieldtype": "Check", - "label": "Included In Paid Amount" + "label": "Considered In Paid Amount" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-05-31 03:41:38.298937", + "modified": "2021-06-09 11:48:25.335733", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Taxes and Charges", diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json index 36fd634b50..170d34e651 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json @@ -198,14 +198,14 @@ "description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry", "fieldname": "included_in_paid_amount", "fieldtype": "Check", - "label": "Included In Paid Amount" + "label": "Considered In Paid Amount" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-05-31 05:40:32.856780", + "modified": "2021-06-09 11:48:04.691596", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index d8d0312ff6..53ded33b6f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -116,6 +116,8 @@ class AccountsController(TransactionBase): if self.doctype == 'Purchase Invoice': self.calculate_paid_amount() + # apply tax withholding only if checked and applicable + self.set_tax_withholding() if self.doctype in ['Purchase Invoice', 'Sales Invoice']: pos_check_field = "is_pos" if self.doctype=="Sales Invoice" else "is_paid" @@ -742,7 +744,8 @@ class AccountsController(TransactionBase): def allocate_advance_taxes(self, gl_entries): tax_map = self.get_tax_map() for pe in self.get("advances"): - if pe.reference_type == "Payment Entry": + if pe.reference_type == "Payment Entry" and \ + frappe.db.get_value('Payment Entry', pe.reference_name, 'advance_tax_account'): pe = frappe.get_doc("Payment Entry", pe.reference_name) for tax in pe.get("taxes"): account_currency = get_account_currency(tax.account_head) From 0b5fe1b5eb0fb24a4a37a6513e76f26b9a1f3bee Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 10 Jun 2021 10:34:49 +0530 Subject: [PATCH 115/180] fix: Failing test case --- .../doctype/purchase_invoice/test_purchase_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index fb4b8d4c1f..c656c8aeec 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1009,8 +1009,8 @@ class TestPurchaseInvoice(unittest.TestCase): ['_Test Account Cost for Goods Sold - _TC', 30000, 0], ['_Test Account Excise Duty - _TC', 0, 3000], ['Creditors - _TC', 0, 27000], - ['TDS Payable - _TC', 3000, 0] - ['TDS Payable - _TC', 0, 3000], + ['TDS Payable - _TC', 3000, 0], + ['TDS Payable - _TC', 0, 3000] ] gl_entries = frappe.db.sql("""select account, debit, credit From edecd5b0c686f653454fa3d85fa87e058ad81f9b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 10 Jun 2021 12:04:30 +0530 Subject: [PATCH 116/180] fix: Update einvoice json test --- .../sales_invoice/test_sales_invoice.py | 98 ++++++++----------- 1 file changed, 41 insertions(+), 57 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index df6d483904..b35686f4f0 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1899,69 +1899,53 @@ class TestSalesInvoice(unittest.TestCase): frappe.flags.country = country def test_einvoice_json(self): - from erpnext.regional.india.e_invoice.utils import make_einvoice + from erpnext.regional.india.e_invoice.utils import make_einvoice, validate_totals - si = make_sales_invoice_for_ewaybill() - si.naming_series = 'INV-2020-.#####' - si.items = [] - si.append("items", { - "item_code": "_Test Item", - "uom": "Nos", - "warehouse": "_Test Warehouse - _TC", - "qty": 2000, - "rate": 12, - "income_account": "Sales - _TC", - "expense_account": "Cost of Goods Sold - _TC", - "cost_center": "_Test Cost Center - _TC", - }) - si.append("items", { - "item_code": "_Test Item 2", - "uom": "Nos", - "warehouse": "_Test Warehouse - _TC", - "qty": 420, - "rate": 15, - "income_account": "Sales - _TC", - "expense_account": "Cost of Goods Sold - _TC", - "cost_center": "_Test Cost Center - _TC", - }) + si = get_sales_invoice_for_e_invoice() si.discount_amount = 100 si.save() einvoice = make_einvoice(si) - - total_item_ass_value = 0 - total_item_cgst_value = 0 - total_item_sgst_value = 0 - total_item_igst_value = 0 - total_item_value = 0 - - for item in einvoice['ItemList']: - total_item_ass_value += item['AssAmt'] - total_item_cgst_value += item['CgstAmt'] - total_item_sgst_value += item['SgstAmt'] - total_item_igst_value += item['IgstAmt'] - total_item_value += item['TotItemVal'] - - self.assertTrue(item['AssAmt'], item['TotAmt'] - item['Discount']) - self.assertTrue(item['TotItemVal'], item['AssAmt'] + item['CgstAmt'] + item['SgstAmt'] + item['IgstAmt']) - - value_details = einvoice['ValDtls'] - - self.assertEqual(einvoice['Version'], '1.1') - self.assertEqual(value_details['AssVal'], total_item_ass_value) - self.assertEqual(value_details['CgstVal'], total_item_cgst_value) - self.assertEqual(value_details['SgstVal'], total_item_sgst_value) - self.assertEqual(value_details['IgstVal'], total_item_igst_value) - - calculated_invoice_value = \ - value_details['AssVal'] + value_details['CgstVal'] \ - + value_details['SgstVal'] + value_details['IgstVal'] \ - + value_details['OthChrg'] - value_details['Discount'] - - self.assertTrue(value_details['TotInvVal'] - calculated_invoice_value < 0.1) - - self.assertEqual(value_details['TotInvVal'], si.base_grand_total) self.assertTrue(einvoice['EwbDtls']) + validate_totals(einvoice) + + si.apply_discount_on = 'Net Total' + si.save() + einvoice = make_einvoice(si) + validate_totals(einvoice) + + [d.set('included_in_print_rate', 1) for d in si.taxes] + si.save() + einvoice = make_einvoice(si) + validate_totals(einvoice) + +def get_sales_invoice_for_e_invoice(): + si = make_sales_invoice_for_ewaybill() + si.naming_series = 'INV-2020-.#####' + si.items = [] + si.append("items", { + "item_code": "_Test Item", + "uom": "Nos", + "warehouse": "_Test Warehouse - _TC", + "qty": 2000, + "rate": 12, + "income_account": "Sales - _TC", + "expense_account": "Cost of Goods Sold - _TC", + "cost_center": "_Test Cost Center - _TC", + }) + + si.append("items", { + "item_code": "_Test Item 2", + "uom": "Nos", + "warehouse": "_Test Warehouse - _TC", + "qty": 420, + "rate": 15, + "income_account": "Sales - _TC", + "expense_account": "Cost of Goods Sold - _TC", + "cost_center": "_Test Cost Center - _TC", + }) + + return si def make_test_address_for_ewaybill(): if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'): From 076bd5eaab52c9ad7805ff511787f8de045743b3 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 10 Jun 2021 12:08:44 +0530 Subject: [PATCH 117/180] fix: Only display GST card in Accounting Workspace if it's in India --- .../workspace/accounting/accounting.json | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json index df68318052..10a4001502 100644 --- a/erpnext/accounts/workspace/accounting/accounting.json +++ b/erpnext/accounts/workspace/accounting/accounting.json @@ -445,15 +445,15 @@ "type": "Link" }, { - "dependencies": "GL Entry", - "hidden": 0, - "is_query_report": 1, - "label": "UAE VAT 201", - "link_to": "UAE VAT 201", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, + "dependencies": "GL Entry", + "hidden": 0, + "is_query_report": 1, + "label": "UAE VAT 201", + "link_to": "UAE VAT 201", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, { "hidden": 0, "is_query_report": 0, @@ -684,6 +684,7 @@ "is_query_report": 0, "label": "Goods and Services Tax (GST India)", "onboard": 0, + "only_for": "India", "type": "Card Break" }, { @@ -694,6 +695,7 @@ "link_to": "GST Settings", "link_type": "DocType", "onboard": 0, + "only_for": "India", "type": "Link" }, { @@ -704,6 +706,7 @@ "link_to": "GST HSN Code", "link_type": "DocType", "onboard": 0, + "only_for": "India", "type": "Link" }, { @@ -714,6 +717,7 @@ "link_to": "GSTR-1", "link_type": "Report", "onboard": 0, + "only_for": "India", "type": "Link" }, { @@ -724,6 +728,7 @@ "link_to": "GSTR-2", "link_type": "Report", "onboard": 0, + "only_for": "India", "type": "Link" }, { @@ -734,6 +739,7 @@ "link_to": "GSTR 3B Report", "link_type": "DocType", "onboard": 0, + "only_for": "India", "type": "Link" }, { @@ -744,6 +750,7 @@ "link_to": "GST Sales Register", "link_type": "Report", "onboard": 0, + "only_for": "India", "type": "Link" }, { @@ -754,6 +761,7 @@ "link_to": "GST Purchase Register", "link_type": "Report", "onboard": 0, + "only_for": "India", "type": "Link" }, { @@ -764,6 +772,7 @@ "link_to": "GST Itemised Sales Register", "link_type": "Report", "onboard": 0, + "only_for": "India", "type": "Link" }, { @@ -774,6 +783,7 @@ "link_to": "GST Itemised Purchase Register", "link_type": "Report", "onboard": 0, + "only_for": "India", "type": "Link" }, { @@ -784,6 +794,7 @@ "link_to": "C-Form", "link_type": "DocType", "onboard": 0, + "only_for": "India", "type": "Link" }, { @@ -794,6 +805,7 @@ "link_to": "Lower Deduction Certificate", "link_type": "DocType", "onboard": 0, + "only_for": "India", "type": "Link" }, { @@ -1052,7 +1064,7 @@ "type": "Link" } ], - "modified": "2021-05-12 11:48:01.905144", + "modified": "2021-06-10 03:17:31.427945", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", @@ -1107,4 +1119,4 @@ "type": "Dashboard" } ] -} +} \ No newline at end of file From d063f9a5d185e9f481991b11c8fa8c0dcf9a39a5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 10 Jun 2021 12:26:21 +0530 Subject: [PATCH 118/180] fix: GL Entry ordering --- .../doctype/purchase_invoice/test_purchase_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index c656c8aeec..723d151ad8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1000,6 +1000,7 @@ class TestPurchaseInvoice(unittest.TestCase): # Create Purchase Invoice against Purchase Order purchase_invoice = get_mapped_purchase_invoice(po.name) purchase_invoice.allocate_advances_automatically = 1 + purchase_invoice.items[0].expense_account = '_Test Account Cost for Goods Sold - _TC' purchase_invoice.save() purchase_invoice.submit() @@ -1009,8 +1010,7 @@ class TestPurchaseInvoice(unittest.TestCase): ['_Test Account Cost for Goods Sold - _TC', 30000, 0], ['_Test Account Excise Duty - _TC', 0, 3000], ['Creditors - _TC', 0, 27000], - ['TDS Payable - _TC', 3000, 0], - ['TDS Payable - _TC', 0, 3000] + ['TDS Payable - _TC', 3000, 3000] ] gl_entries = frappe.db.sql("""select account, debit, credit From aedf25ba46d20d8a7d40b6c77f880cfdc426cf90 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Jun 2021 11:44:40 +0530 Subject: [PATCH 119/180] chore: Drop old patches v7 backup was restored and upgraded to latest v10.x.x branch. The patches run uptil the upgrade are removed in this change. This means only existing v10 sites are allowed direct upgrade to v13 and newer --- erpnext/patches/repair_tools/__init__.py | 0 .../set_stock_balance_as_per_serial_no.py | 13 - erpnext/patches/v10_0/__init__.py | 0 .../patches/v10_0/add_agriculture_domain.py | 13 - .../add_guardian_role_for_parent_portal.py | 23 -- .../patches/v10_0/add_non_profit_domain.py | 20 -- .../allow_operators_in_supplier_scorecard.py | 23 -- .../v10_0/copy_projects_renamed_fields.py | 13 - ..._regional_print_format_based_on_country.py | 22 -- .../fix_reserved_qty_for_sub_contract.py | 31 --- .../recalculate_gross_margin_for_project.py | 14 - .../v10_0/rename_schools_to_education.py | 32 --- ...r_purchase_receipts_with_rejected_items.py | 32 --- ...t_requested_qty_for_non_stock_uom_items.py | 21 -- ...t_auto_created_serial_no_in_stock_entry.py | 56 ---- erpnext/patches/v10_0/set_b2c_limit.py | 12 - ..._default_payment_terms_based_on_company.py | 37 --- erpnext/patches/v10_0/set_discount_amount.py | 35 --- ...set_numeric_ranges_in_template_if_blank.py | 35 --- .../v10_0/set_primary_contact_for_customer.py | 21 -- ...n_transactions_based_on_serial_no_input.py | 21 -- .../patches/v10_0/set_student_party_type.py | 8 - .../setup_vat_for_uae_and_saudi_arabia.py | 13 - ...s_of_all_department_members_in_calendar.py | 6 - erpnext/patches/v10_0/taxes_issue_with_pos.py | 26 -- .../update_address_template_for_india.py | 12 - .../patches/v10_0/update_assessment_plan.py | 17 -- .../patches/v10_0/update_assessment_result.py | 20 -- .../update_asset_calculate_depreciation.py | 12 - .../v10_0/update_hub_connector_domain.py | 10 - .../v10_0/update_lft_rgt_for_employee.py | 9 - .../patches/v10_0/update_project_in_sle.py | 15 -- .../update_reserved_qty_for_purchase_order.py | 53 ---- ...date_sales_order_link_to_purchase_order.py | 18 -- ...update_status_for_multiple_source_in_po.py | 40 --- .../update_status_in_purchase_receipt.py | 8 - .../update_territory_and_customer_group.py | 29 --- .../v10_0/update_user_image_in_employee.py | 19 -- .../v10_0/update_warehouse_address_details.py | 37 --- erpnext/patches/v10_1/__init__.py | 0 erpnext/patches/v11_0/__init__.py | 1 - .../v11_0/remove_subscriber_doctype.py | 16 -- erpnext/patches/v11_1/__init__.py | 0 erpnext/patches/v12_0/__init__.py | 0 erpnext/patches/v13_0/__init__.py | 1 - erpnext/patches/v4_0/__init__.py | 0 .../patches/v4_0/apply_user_permissions.py | 50 ---- erpnext/patches/v4_0/countrywise_coa.py | 29 --- ...custom_fields_for_india_specific_fields.py | 63 ----- .../v4_0/create_price_list_if_missing.py | 35 --- .../v4_0/customer_discount_to_pricing_rule.py | 33 --- erpnext/patches/v4_0/fields_to_be_renamed.py | 109 -------- erpnext/patches/v4_0/fix_address_template.py | 12 - .../patches/v4_0/fix_case_of_hr_module_def.py | 13 - erpnext/patches/v4_0/fix_contact_address.py | 13 - erpnext/patches/v4_0/fix_employee_user_id.py | 23 -- .../global_defaults_to_system_settings.py | 39 --- erpnext/patches/v4_0/import_country_codes.py | 15 -- .../v4_0/map_charge_to_taxes_and_charges.py | 16 -- .../move_warehouse_user_to_restrictions.py | 13 - erpnext/patches/v4_0/new_address_template.py | 14 - .../patches/v4_0/reload_sales_print_format.py | 8 - .../remove_employee_role_if_no_employee.py | 18 -- .../patches/v4_0/remove_module_home_pages.py | 10 - .../patches/v4_0/rename_sitemap_to_route.py | 17 -- .../v4_0/reset_permissions_for_masters.py | 20 -- .../patches/v4_0/save_default_letterhead.py | 13 - .../set_pricing_rule_for_buying_or_selling.py | 13 - erpnext/patches/v4_0/split_email_settings.py | 67 ----- .../patches/v4_0/update_account_root_type.py | 34 --- ...custom_print_formats_for_renamed_fields.py | 36 --- ...to_sales_person_in_maintenance_schedule.py | 12 - ...harges_in_custom_purchase_print_formats.py | 12 - .../v4_0/update_tax_amount_after_discount.py | 20 -- .../patches/v4_0/update_user_properties.py | 51 ---- .../v4_0/update_users_report_view_settings.py | 12 - erpnext/patches/v4_0/validate_v3_patch.py | 11 - erpnext/patches/v4_1/__init__.py | 0 .../v4_1/fix_delivery_and_billing_status.py | 12 - erpnext/patches/v4_1/fix_jv_remarks.py | 21 -- .../v4_1/fix_sales_order_delivered_status.py | 15 -- .../patches/v4_1/set_outgoing_email_footer.py | 12 - erpnext/patches/v4_2/__init__.py | 0 .../patches/v4_2/add_currency_turkish_lira.py | 10 - erpnext/patches/v4_2/default_website_style.py | 11 - ...elete_gl_entries_for_cancelled_invoices.py | 14 - .../patches/v4_2/delete_old_print_formats.py | 23 -- erpnext/patches/v4_2/discount_amount.py | 12 - .../patches/v4_2/fix_account_master_type.py | 12 - .../fix_gl_entries_for_stock_transactions.py | 54 ---- erpnext/patches/v4_2/fix_recurring_orders.py | 41 --- erpnext/patches/v4_2/party_model.py | 117 --------- erpnext/patches/v4_2/recalculate_bom_cost.py | 16 -- .../repost_sle_for_si_with_no_warehouse.py | 34 --- .../v4_2/repost_stock_reconciliation.py | 31 --- erpnext/patches/v4_2/reset_bom_costs.py | 17 -- .../v4_2/seprate_manufacture_and_repack.py | 9 - erpnext/patches/v4_2/set_company_country.py | 15 -- erpnext/patches/v4_2/set_item_has_batch.py | 65 ----- erpnext/patches/v4_2/toggle_rounded_total.py | 9 - .../v4_2/update_landed_cost_voucher.py | 10 - .../patches/v4_2/update_project_milestones.py | 9 - .../update_sales_order_invoice_field_name.py | 7 - .../v4_2/update_stock_uom_for_dn_in_sle.py | 11 - erpnext/patches/v4_4/__init__.py | 0 erpnext/patches/v4_4/make_email_accounts.py | 96 ------- erpnext/patches/v5_0/__init__.py | 0 .../v5_0/convert_stock_reconciliation.py | 31 --- .../patches/v5_0/execute_on_doctype_update.py | 9 - .../fix_taxes_and_totals_in_party_currency.py | 66 ----- .../v5_0/index_on_account_and_gl_entry.py | 30 --- erpnext/patches/v5_0/is_group.py | 9 - erpnext/patches/v5_0/item_patches.py | 6 - .../v5_0/link_warehouse_with_account.py | 10 - erpnext/patches/v5_0/new_crm_module.py | 24 -- erpnext/patches/v5_0/newsletter.py | 38 --- .../v5_0/opportunity_not_submittable.py | 10 - erpnext/patches/v5_0/party_model_patch_fix.py | 18 -- erpnext/patches/v5_0/portal_fixes.py | 7 - erpnext/patches/v5_0/project_costing.py | 8 - .../v5_0/recalculate_total_amount_in_jv.py | 26 -- ...nned_operating_cost_in_production_order.py | 13 - .../patches/v5_0/remove_birthday_events.py | 7 - erpnext/patches/v5_0/rename_customer_issue.py | 6 - erpnext/patches/v5_0/rename_pos_setting.py | 6 - .../patches/v5_0/rename_table_fieldnames.py | 243 ------------------ .../v5_0/rename_taxes_and_charges_master.py | 14 - erpnext/patches/v5_0/rename_total_fields.py | 55 ---- ...lds_in_custom_scripts_and_print_formats.py | 65 ----- .../repost_gle_for_jv_with_multiple_party.py | 26 -- erpnext/patches/v5_0/repost_requested_qty.py | 21 -- erpnext/patches/v5_0/reset_values_in_tools.py | 12 - erpnext/patches/v5_0/set_appraisal_remarks.py | 9 - .../v5_0/set_default_company_in_bom.py | 10 - erpnext/patches/v5_0/set_footer_address.py | 9 - .../patches/v5_0/stock_entry_update_value.py | 8 - .../taxes_and_totals_in_party_currency.py | 80 ------ erpnext/patches/v5_0/update_account_types.py | 20 -- erpnext/patches/v5_0/update_advance_paid.py | 13 - .../update_companywise_payment_account.py | 21 -- .../v5_0/update_dn_against_doc_fields.py | 14 - erpnext/patches/v5_0/update_from_bom.py | 9 - .../update_frozen_accounts_permission_role.py | 13 - .../v5_0/update_item_and_description_again.py | 50 ---- .../v5_0/update_item_desc_in_invoice.py | 52 ---- .../v5_0/update_item_description_and_image.py | 54 ---- .../patches/v5_0/update_item_name_in_bom.py | 18 -- .../v5_0/update_journal_entry_title.py | 12 - ...pdate_material_transfer_for_manufacture.py | 6 - ..._material_transferred_for_manufacturing.py | 10 - ...ial_transferred_for_manufacturing_again.py | 19 -- .../v5_0/update_operation_description.py | 11 - erpnext/patches/v5_0/update_opportunity.py | 14 - erpnext/patches/v5_0/update_projects.py | 34 --- erpnext/patches/v5_0/update_sms_sender.py | 9 - ...amount_after_discount_in_purchase_cycle.py | 17 -- .../patches/v5_0/update_temporary_account.py | 9 - erpnext/patches/v5_0/update_time_log_title.py | 12 - erpnext/patches/v5_1/__init__.py | 0 erpnext/patches/v5_1/default_bom.py | 7 - erpnext/patches/v5_1/fix_against_account.py | 37 --- erpnext/patches/v5_1/rename_roles.py | 10 - erpnext/patches/v5_1/sales_bom_rename.py | 12 - erpnext/patches/v5_2/__init__.py | 1 - .../v5_2/change_item_selects_to_checks.py | 19 -- erpnext/patches/v5_4/__init__.py | 0 erpnext/patches/v5_4/cleanup_journal_entry.py | 21 -- .../patches/v5_4/fix_invoice_outstanding.py | 13 - .../patches/v5_4/fix_missing_item_images.py | 126 --------- ...x_reserved_qty_and_sle_for_packed_items.py | 22 -- ...anagers_regarding_wrong_tax_calculation.py | 41 --- .../patches/v5_4/set_root_and_report_type.py | 12 - .../v5_4/stock_entry_additional_costs.py | 53 ---- .../update_purchase_cost_against_project.py | 13 - erpnext/patches/v5_7/__init__.py | 1 - .../patches/v5_7/item_template_attributes.py | 124 --------- erpnext/patches/v5_8/__init__.py | 1 - .../v5_8/add_credit_note_print_heading.py | 14 - erpnext/patches/v5_8/tax_rule.py | 31 --- ...pdate_order_reference_in_return_entries.py | 92 ------- erpnext/patches/v6_0/__init__.py | 0 erpnext/patches/v6_0/default_activity_rate.py | 14 - .../patches/v6_0/fix_outstanding_amount.py | 16 -- erpnext/patches/v6_0/fix_planned_qty.py | 14 - erpnext/patches/v6_0/multi_currency.py | 65 ----- erpnext/patches/v6_0/set_default_title.py | 36 --- erpnext/patches/v6_10/__init__.py | 1 - .../v6_10/email_digest_default_quote.py | 6 - .../fix_billed_amount_in_drop_ship_po.py | 18 -- .../fix_delivery_status_of_drop_ship_item.py | 10 - erpnext/patches/v6_10/fix_jv_total_amount.py | 14 - .../v6_10/fix_ordered_received_billed.py | 17 -- erpnext/patches/v6_12/__init__.py | 0 .../repost_entries_with_target_warehouse.py | 175 ------------- erpnext/patches/v6_12/set_overdue_tasks.py | 8 - erpnext/patches/v6_16/__init__.py | 1 - .../v6_16/create_manufacturer_records.py | 19 -- .../update_billing_status_in_dn_and_pr.py | 42 --- erpnext/patches/v6_19/__init__.py | 1 - .../v6_19/comment_feed_communication.py | 7 - erpnext/patches/v6_2/__init__.py | 1 - .../fix_missing_default_taxes_and_lead.py | 25 -- .../v6_2/remove_newsletter_duplicates.py | 13 - erpnext/patches/v6_20/__init__.py | 0 .../set_party_account_currency_in_orders.py | 24 -- erpnext/patches/v6_20x/__init__.py | 1 - .../v6_20x/remove_customer_supplier_roles.py | 23 -- .../remove_fiscal_year_from_holiday_list.py | 19 -- .../v6_20x/rename_project_name_to_project.py | 17 -- ...t_valuation_rate_for_negative_inventory.py | 11 - erpnext/patches/v6_20x/set_compact_print.py | 8 - .../update_product_bundle_description.py | 11 - erpnext/patches/v6_21/__init__.py | 1 - erpnext/patches/v6_21/fix_reorder_level.py | 24 -- .../v6_21/rename_material_request_fields.py | 14 - erpnext/patches/v6_23/__init__.py | 0 .../v6_23/update_stopped_status_to_closed.py | 9 - erpnext/patches/v6_24/__init__.py | 0 ...tomer_address_to_shipping_address_on_po.py | 19 -- erpnext/patches/v6_24/set_recurring_id.py | 13 - erpnext/patches/v6_27/__init__.py | 1 - .../v6_27/fix_recurring_order_status.py | 54 ---- erpnext/patches/v6_3/__init__.py | 0 .../v6_3/convert_applicable_territory.py | 24 -- erpnext/patches/v6_4/__init__.py | 1 - erpnext/patches/v6_4/email_digest_update.py | 10 - erpnext/patches/v6_4/fix_duplicate_bins.py | 20 -- .../v6_4/fix_expense_included_in_valuation.py | 74 ------ ...x_journal_entries_due_to_reconciliation.py | 53 ---- ...ified_in_sales_order_and_purchase_order.py | 10 - .../fix_sales_order_maintenance_status.py | 8 - .../fix_status_in_sales_and_purchase_order.py | 8 - erpnext/patches/v6_4/make_image_thumbnail.py | 15 -- ...al_entries_where_reference_name_missing.py | 23 -- .../v6_4/round_status_updater_percentages.py | 14 - erpnext/patches/v6_4/set_user_in_contact.py | 7 - erpnext/patches/v6_5/__init__.py | 1 - .../v6_5/show_in_website_for_template_item.py | 15 -- erpnext/patches/v6_6/__init__.py | 1 - erpnext/patches/v6_6/fix_website_image.py | 32 --- ...emove_fiscal_year_from_leave_allocation.py | 17 -- erpnext/patches/v6_8/__init__.py | 0 erpnext/patches/v6_8/make_webform_standard.py | 14 - .../v6_8/move_drop_ship_to_po_items.py | 43 ---- erpnext/patches/v7_0/__init__.py | 0 .../v7_0/calculate_total_costing_amount.py | 18 -- .../v7_0/convert_timelog_to_timesheet.py | 69 ----- .../v7_0/convert_timelogbatch_to_timesheet.py | 32 --- erpnext/patches/v7_0/create_budget_record.py | 57 ---- .../v7_0/create_warehouse_nestedset.py | 128 --------- erpnext/patches/v7_0/fix_duplicate_icons.py | 27 -- ...ouse_ledger_gl_entries_for_transactions.py | 51 ---- erpnext/patches/v7_0/make_guardian.py | 37 --- .../v7_0/make_is_group_fieldtype_as_check.py | 18 -- ...count_type_stock_and_warehouse_to_stock.py | 10 - .../v7_0/migrate_mode_of_payments_v6_to_v7.py | 38 --- .../v7_0/migrate_schools_to_erpnext.py | 30 --- ...lesinvoiceitem_to_salesinvoicetimesheet.py | 17 -- .../v7_0/po_status_issue_for_pr_return.py | 40 --- erpnext/patches/v7_0/re_route.py | 5 - .../remove_administrator_role_in_doctypes.py | 9 - .../v7_0/remove_doctypes_and_reports.py | 27 -- erpnext/patches/v7_0/remove_features_setup.py | 29 --- .../remove_old_earning_deduction_doctypes.py | 16 -- .../v7_0/rename_advance_table_fields.py | 18 -- .../v7_0/rename_examination_to_assessment.py | 24 -- .../rename_fee_amount_to_fee_component.py | 16 -- erpnext/patches/v7_0/rename_prevdoc_fields.py | 76 ------ .../patches/v7_0/rename_salary_components.py | 149 ----------- .../patches/v7_0/rename_time_sheet_doctype.py | 14 - .../repost_bin_qty_and_item_projected_qty.py | 15 -- .../repost_gle_for_pi_with_update_stock.py | 20 -- .../v7_0/repost_gle_for_pos_sales_return.py | 25 -- ...et_base_amount_in_invoice_payment_table.py | 24 -- .../v7_0/set_is_group_for_warehouse.py | 7 - .../v7_0/set_material_request_type_in_item.py | 16 -- .../v7_0/set_naming_series_for_timesheet.py | 15 -- .../v7_0/set_party_name_in_payment_entry.py | 20 -- erpnext/patches/v7_0/set_portal_settings.py | 28 -- ..._table_for_expense_claim_type_if_exists.py | 20 -- .../v7_0/system_settings_setup_complete.py | 16 -- erpnext/patches/v7_0/update_autoname_field.py | 14 - .../v7_0/update_change_amount_account.py | 19 -- ...rsion_factor_in_supplier_quotation_item.py | 19 -- erpnext/patches/v7_0/update_home_page.py | 27 -- .../update_maintenance_module_in_doctype.py | 11 - .../v7_0/update_mins_to_first_response.py | 24 -- .../update_missing_employee_in_timesheet.py | 20 -- .../v7_0/update_mode_of_payment_type.py | 29 --- erpnext/patches/v7_0/update_party_status.py | 21 -- ...vdoc_values_for_supplier_quotation_item.py | 9 - .../v7_0/update_project_in_gl_entry.py | 21 -- .../update_refdoc_in_landed_cost_voucher.py | 15 -- .../v7_0/update_status_for_timesheet.py | 11 - .../patches/v7_0/update_status_of_po_so.py | 68 ----- ...pdate_status_of_zero_amount_sales_order.py | 7 - .../v7_0/update_timesheet_communications.py | 27 -- erpnext/patches/v7_1/__init__.py | 1 - .../add_account_user_role_for_timesheet.py | 31 --- .../v7_1/add_field_for_task_dependent.py | 10 - .../v7_1/fix_link_for_customer_from_lead.py | 7 - ..._invoice_from_parent_to_child_timesheet.py | 20 -- .../patches/v7_1/rename_field_timesheet.py | 11 - .../v7_1/rename_quality_inspection_field.py | 38 --- ...tock_for_deleted_bins_for_merging_items.py | 44 ---- erpnext/patches/v7_1/save_stock_settings.py | 15 -- .../v7_1/set_budget_against_as_cost_center.py | 11 - .../v7_1/set_currency_exchange_date.py | 10 - .../v7_1/set_prefered_contact_email.py | 17 -- .../patches/v7_1/set_sales_person_status.py | 8 - erpnext/patches/v7_1/set_student_guardian.py | 23 -- .../v7_1/set_total_amount_currency_in_je.py | 24 -- .../patches/v7_1/update_bom_base_currency.py | 20 -- erpnext/patches/v7_1/update_component_type.py | 15 -- erpnext/patches/v7_1/update_invoice_status.py | 34 --- erpnext/patches/v7_1/update_lead_source.py | 29 --- .../update_missing_salary_component_type.py | 50 ---- erpnext/patches/v7_1/update_portal_roles.py | 21 -- .../v7_1/update_total_billing_hours.py | 14 - erpnext/patches/v7_2/__init__.py | 1 - ...ar_leave_encashment_as_salary_component.py | 36 --- erpnext/patches/v7_2/contact_address_links.py | 32 --- .../delete_fleet_management_module_def.py | 10 - ...ty_supplied_items_for_non_subcontracted.py | 14 - .../patches/v7_2/make_all_assessment_group.py | 14 - erpnext/patches/v7_2/mark_students_active.py | 9 - .../v7_2/rename_att_date_attendance.py | 15 -- .../v7_2/rename_evaluation_criteria.py | 39 --- .../patches/v7_2/set_null_value_to_fields.py | 11 - .../patches/v7_2/setup_auto_close_settings.py | 18 -- erpnext/patches/v7_2/stock_uom_in_selling.py | 15 -- .../v7_2/update_abbr_in_salary_slips.py | 14 - .../patches/v7_2/update_assessment_modules.py | 51 ---- .../v7_2/update_attendance_docstatus.py | 10 - erpnext/patches/v7_2/update_doctype_status.py | 11 - .../update_guardian_name_in_student_master.py | 14 - erpnext/patches/v7_2/update_party_type.py | 16 -- erpnext/patches/v7_2/update_salary_slips.py | 22 -- .../v7_2/update_website_for_variant.py | 13 - erpnext/patches/v8_0/__init__.py | 1 - .../patches/v8_0/addresses_linked_to_lead.py | 5 - .../v8_0/change_in_words_varchar_length.py | 16 -- ...dress_doc_from_address_field_in_company.py | 33 --- erpnext/patches/v8_0/create_domain_docs.py | 53 ---- erpnext/patches/v8_0/delete_bin_indexes.py | 16 -- .../delete_schools_depricated_doctypes.py | 14 - .../patches/v8_0/disable_instructor_role.py | 18 -- ...ooking_asset_depreciation_automatically.py | 9 - ..._for_invoices_with_negative_outstanding.py | 23 -- ...ayments_table_blank_for_non_pos_invoice.py | 15 -- .../merge_student_batch_and_student_group.py | 73 ------ ...from_account_to_warehouse_for_inventory.py | 15 -- .../v8_0/move_perpetual_inventory_setting.py | 13 - ...ample_item_to_allow_zero_valuation_rate.py | 13 - ...ems_in_status_field_of_material_request.py | 25 -- ...rename_total_margin_to_rate_with_margin.py | 24 -- ...ost_reserved_qty_for_multiple_sales_uom.py | 19 -- .../revert_manufacturers_table_from_item.py | 22 -- erpnext/patches/v8_0/save_system_settings.py | 20 -- ..._serial_nos_for_disabled_sales_invoices.py | 14 - .../patches/v8_0/set_project_copied_from.py | 11 - ...nvoice_serial_number_from_delivery_note.py | 42 --- .../patches/v8_0/update_customer_pos_id.py | 9 - .../patches/v8_0/update_production_orders.py | 49 ---- .../v8_0/update_sales_cost_in_project.py | 11 - ...tus_as_paid_for_completed_expense_claim.py | 19 -- .../update_stock_qty_value_in_bom_item.py | 14 - ...ate_stock_qty_value_in_purchase_invoice.py | 9 - ...ate_student_groups_from_student_batches.py | 38 --- .../update_supplier_address_in_stock_entry.py | 22 -- erpnext/patches/v8_1/__init__.py | 0 erpnext/patches/v8_1/add_hsn_sac_codes.py | 11 - .../add_indexes_in_transaction_doctypes.py | 10 - ...allow_invoice_copy_to_edit_after_submit.py | 13 - .../patches/v8_1/delete_deprecated_reports.py | 33 --- erpnext/patches/v8_1/gst_fixes.py | 62 ----- ...e_sales_invoice_from_returned_serial_no.py | 18 -- .../v8_1/removed_report_support_hours.py | 14 - .../v8_1/set_delivery_date_in_so_item.py | 22 -- .../v8_1/update_expense_claim_status.py | 23 -- erpnext/patches/v8_1/update_gst_state.py | 15 -- erpnext/patches/v8_10/__init__.py | 0 .../change_default_customer_credit_days.py | 89 ------- erpnext/patches/v8_3/__init__.py | 0 .../set_restrict_to_domain_for_module_def.py | 9 - .../v8_3/update_company_total_sales.py | 15 -- erpnext/patches/v8_4/__init__.py | 1 - .../patches/v8_4/make_scorecard_records.py | 11 - erpnext/patches/v8_5/__init__.py | 0 .../fix_tax_breakup_for_non_invoice_docs.py | 48 ---- .../remove_project_type_property_setter.py | 18 -- .../remove_quotations_route_in_sidebar.py | 16 -- .../v8_5/set_default_mode_of_payment.py | 17 -- .../update_customer_group_in_POS_profile.py | 9 - .../update_existing_data_in_project_type.py | 19 -- erpnext/patches/v8_6/__init__.py | 0 ...point_sms_doctype_module_to_frappe_core.py | 9 - .../patches/v8_6/rename_bom_update_tool.py | 9 - ...mission_for_quotation_for_sales_manager.py | 11 - .../v8_6/update_timesheet_company_from_PO.py | 15 -- erpnext/patches/v8_7/__init__.py | 0 .../v8_7/fix_purchase_receipt_status.py | 13 - .../make_subscription_from_recurring_data.py | 58 ----- erpnext/patches/v8_8/__init__.py | 0 .../add_new_fields_in_accounts_settings.py | 9 - .../patches/v8_8/set_bom_rate_as_per_uom.py | 13 - erpnext/patches/v8_9/__init__.py | 1 - .../v8_9/add_setup_progress_actions.py | 47 ---- ...gst_doctypes_for_outside_india_accounts.py | 14 - ...e_employee_from_salary_structure_parent.py | 6 - .../v8_9/rename_company_sales_target_field.py | 8 - .../v8_9/set_default_customer_group.py | 8 - .../set_default_fields_in_variant_settings.py | 13 - erpnext/patches/v8_9/set_member_party_type.py | 9 - .../v8_9/set_print_zero_amount_taxes.py | 9 - ...update_billing_gstin_for_indian_account.py | 15 -- erpnext/patches/v9_0/__init__.py | 1 - erpnext/patches/v9_0/add_healthcare_domain.py | 13 - .../add_user_to_child_table_in_pos_profile.py | 38 --- .../patches/v9_0/copy_old_fees_field_data.py | 15 -- ..._existing_warehouse_from_stock_settings.py | 8 - .../v9_0/remove_subscription_module.py | 9 - .../v9_0/revert_manufacturing_user_role.py | 22 -- erpnext/patches/v9_0/set_pos_profile_name.py | 24 -- ...for_material_request_and_purchase_order.py | 24 -- ...ipping_type_for_existing_shipping_rules.py | 18 -- .../patches/v9_0/set_uoms_in_variant_field.py | 14 - .../v9_0/set_variant_item_description.py | 46 ---- .../student_admission_childtable_migrate.py | 31 --- .../v9_0/update_employee_loan_details.py | 24 -- ...te_multi_uom_fields_in_material_request.py | 12 - erpnext/patches/v9_1/__init__.py | 0 .../v9_1/create_issue_opportunity_type.py | 34 --- erpnext/patches/v9_2/__init__.py | 0 .../delete_healthcare_domain_default_items.py | 17 -- .../patches/v9_2/delete_process_payroll.py | 5 - .../v9_2/remove_company_from_patient.py | 7 - .../v9_2/rename_net_weight_in_item_master.py | 8 - .../v9_2/rename_translated_domains_in_en.py | 39 --- .../repost_reserved_qty_for_production.py | 9 - .../v9_2/set_item_name_in_production_order.py | 12 - 441 files changed, 9599 deletions(-) delete mode 100644 erpnext/patches/repair_tools/__init__.py delete mode 100644 erpnext/patches/repair_tools/set_stock_balance_as_per_serial_no.py delete mode 100644 erpnext/patches/v10_0/__init__.py delete mode 100644 erpnext/patches/v10_0/add_agriculture_domain.py delete mode 100644 erpnext/patches/v10_0/add_guardian_role_for_parent_portal.py delete mode 100644 erpnext/patches/v10_0/add_non_profit_domain.py delete mode 100644 erpnext/patches/v10_0/allow_operators_in_supplier_scorecard.py delete mode 100644 erpnext/patches/v10_0/copy_projects_renamed_fields.py delete mode 100644 erpnext/patches/v10_0/enabled_regional_print_format_based_on_country.py delete mode 100644 erpnext/patches/v10_0/fix_reserved_qty_for_sub_contract.py delete mode 100644 erpnext/patches/v10_0/recalculate_gross_margin_for_project.py delete mode 100644 erpnext/patches/v10_0/rename_schools_to_education.py delete mode 100644 erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py delete mode 100644 erpnext/patches/v10_0/repost_requested_qty_for_non_stock_uom_items.py delete mode 100644 erpnext/patches/v10_0/set_auto_created_serial_no_in_stock_entry.py delete mode 100644 erpnext/patches/v10_0/set_b2c_limit.py delete mode 100644 erpnext/patches/v10_0/set_default_payment_terms_based_on_company.py delete mode 100644 erpnext/patches/v10_0/set_discount_amount.py delete mode 100644 erpnext/patches/v10_0/set_numeric_ranges_in_template_if_blank.py delete mode 100644 erpnext/patches/v10_0/set_primary_contact_for_customer.py delete mode 100644 erpnext/patches/v10_0/set_qty_in_transactions_based_on_serial_no_input.py delete mode 100644 erpnext/patches/v10_0/set_student_party_type.py delete mode 100644 erpnext/patches/v10_0/setup_vat_for_uae_and_saudi_arabia.py delete mode 100644 erpnext/patches/v10_0/show_leaves_of_all_department_members_in_calendar.py delete mode 100644 erpnext/patches/v10_0/taxes_issue_with_pos.py delete mode 100644 erpnext/patches/v10_0/update_address_template_for_india.py delete mode 100644 erpnext/patches/v10_0/update_assessment_plan.py delete mode 100644 erpnext/patches/v10_0/update_assessment_result.py delete mode 100644 erpnext/patches/v10_0/update_asset_calculate_depreciation.py delete mode 100644 erpnext/patches/v10_0/update_hub_connector_domain.py delete mode 100644 erpnext/patches/v10_0/update_lft_rgt_for_employee.py delete mode 100644 erpnext/patches/v10_0/update_project_in_sle.py delete mode 100644 erpnext/patches/v10_0/update_reserved_qty_for_purchase_order.py delete mode 100644 erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py delete mode 100644 erpnext/patches/v10_0/update_status_for_multiple_source_in_po.py delete mode 100644 erpnext/patches/v10_0/update_status_in_purchase_receipt.py delete mode 100644 erpnext/patches/v10_0/update_territory_and_customer_group.py delete mode 100644 erpnext/patches/v10_0/update_user_image_in_employee.py delete mode 100644 erpnext/patches/v10_0/update_warehouse_address_details.py delete mode 100644 erpnext/patches/v10_1/__init__.py delete mode 100644 erpnext/patches/v11_0/__init__.py delete mode 100644 erpnext/patches/v11_0/remove_subscriber_doctype.py delete mode 100644 erpnext/patches/v11_1/__init__.py delete mode 100644 erpnext/patches/v12_0/__init__.py delete mode 100644 erpnext/patches/v13_0/__init__.py delete mode 100644 erpnext/patches/v4_0/__init__.py delete mode 100644 erpnext/patches/v4_0/apply_user_permissions.py delete mode 100644 erpnext/patches/v4_0/countrywise_coa.py delete mode 100644 erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py delete mode 100644 erpnext/patches/v4_0/create_price_list_if_missing.py delete mode 100644 erpnext/patches/v4_0/customer_discount_to_pricing_rule.py delete mode 100644 erpnext/patches/v4_0/fields_to_be_renamed.py delete mode 100644 erpnext/patches/v4_0/fix_address_template.py delete mode 100644 erpnext/patches/v4_0/fix_case_of_hr_module_def.py delete mode 100644 erpnext/patches/v4_0/fix_contact_address.py delete mode 100644 erpnext/patches/v4_0/fix_employee_user_id.py delete mode 100644 erpnext/patches/v4_0/global_defaults_to_system_settings.py delete mode 100644 erpnext/patches/v4_0/import_country_codes.py delete mode 100644 erpnext/patches/v4_0/map_charge_to_taxes_and_charges.py delete mode 100644 erpnext/patches/v4_0/move_warehouse_user_to_restrictions.py delete mode 100644 erpnext/patches/v4_0/new_address_template.py delete mode 100644 erpnext/patches/v4_0/reload_sales_print_format.py delete mode 100644 erpnext/patches/v4_0/remove_employee_role_if_no_employee.py delete mode 100644 erpnext/patches/v4_0/remove_module_home_pages.py delete mode 100644 erpnext/patches/v4_0/rename_sitemap_to_route.py delete mode 100644 erpnext/patches/v4_0/reset_permissions_for_masters.py delete mode 100644 erpnext/patches/v4_0/save_default_letterhead.py delete mode 100644 erpnext/patches/v4_0/set_pricing_rule_for_buying_or_selling.py delete mode 100644 erpnext/patches/v4_0/split_email_settings.py delete mode 100644 erpnext/patches/v4_0/update_account_root_type.py delete mode 100644 erpnext/patches/v4_0/update_custom_print_formats_for_renamed_fields.py delete mode 100644 erpnext/patches/v4_0/update_incharge_name_to_sales_person_in_maintenance_schedule.py delete mode 100644 erpnext/patches/v4_0/update_other_charges_in_custom_purchase_print_formats.py delete mode 100644 erpnext/patches/v4_0/update_tax_amount_after_discount.py delete mode 100644 erpnext/patches/v4_0/update_user_properties.py delete mode 100644 erpnext/patches/v4_0/update_users_report_view_settings.py delete mode 100644 erpnext/patches/v4_0/validate_v3_patch.py delete mode 100644 erpnext/patches/v4_1/__init__.py delete mode 100644 erpnext/patches/v4_1/fix_delivery_and_billing_status.py delete mode 100644 erpnext/patches/v4_1/fix_jv_remarks.py delete mode 100644 erpnext/patches/v4_1/fix_sales_order_delivered_status.py delete mode 100644 erpnext/patches/v4_1/set_outgoing_email_footer.py delete mode 100644 erpnext/patches/v4_2/__init__.py delete mode 100644 erpnext/patches/v4_2/add_currency_turkish_lira.py delete mode 100644 erpnext/patches/v4_2/default_website_style.py delete mode 100644 erpnext/patches/v4_2/delete_gl_entries_for_cancelled_invoices.py delete mode 100644 erpnext/patches/v4_2/delete_old_print_formats.py delete mode 100644 erpnext/patches/v4_2/discount_amount.py delete mode 100644 erpnext/patches/v4_2/fix_account_master_type.py delete mode 100644 erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py delete mode 100644 erpnext/patches/v4_2/fix_recurring_orders.py delete mode 100644 erpnext/patches/v4_2/party_model.py delete mode 100644 erpnext/patches/v4_2/recalculate_bom_cost.py delete mode 100644 erpnext/patches/v4_2/repost_sle_for_si_with_no_warehouse.py delete mode 100644 erpnext/patches/v4_2/repost_stock_reconciliation.py delete mode 100644 erpnext/patches/v4_2/reset_bom_costs.py delete mode 100644 erpnext/patches/v4_2/seprate_manufacture_and_repack.py delete mode 100644 erpnext/patches/v4_2/set_company_country.py delete mode 100644 erpnext/patches/v4_2/set_item_has_batch.py delete mode 100644 erpnext/patches/v4_2/toggle_rounded_total.py delete mode 100644 erpnext/patches/v4_2/update_landed_cost_voucher.py delete mode 100644 erpnext/patches/v4_2/update_project_milestones.py delete mode 100644 erpnext/patches/v4_2/update_sales_order_invoice_field_name.py delete mode 100644 erpnext/patches/v4_2/update_stock_uom_for_dn_in_sle.py delete mode 100644 erpnext/patches/v4_4/__init__.py delete mode 100644 erpnext/patches/v4_4/make_email_accounts.py delete mode 100644 erpnext/patches/v5_0/__init__.py delete mode 100644 erpnext/patches/v5_0/convert_stock_reconciliation.py delete mode 100644 erpnext/patches/v5_0/execute_on_doctype_update.py delete mode 100644 erpnext/patches/v5_0/fix_taxes_and_totals_in_party_currency.py delete mode 100644 erpnext/patches/v5_0/index_on_account_and_gl_entry.py delete mode 100644 erpnext/patches/v5_0/is_group.py delete mode 100644 erpnext/patches/v5_0/item_patches.py delete mode 100644 erpnext/patches/v5_0/link_warehouse_with_account.py delete mode 100644 erpnext/patches/v5_0/new_crm_module.py delete mode 100644 erpnext/patches/v5_0/newsletter.py delete mode 100644 erpnext/patches/v5_0/opportunity_not_submittable.py delete mode 100644 erpnext/patches/v5_0/party_model_patch_fix.py delete mode 100644 erpnext/patches/v5_0/portal_fixes.py delete mode 100644 erpnext/patches/v5_0/project_costing.py delete mode 100644 erpnext/patches/v5_0/recalculate_total_amount_in_jv.py delete mode 100644 erpnext/patches/v5_0/reclculate_planned_operating_cost_in_production_order.py delete mode 100644 erpnext/patches/v5_0/remove_birthday_events.py delete mode 100644 erpnext/patches/v5_0/rename_customer_issue.py delete mode 100644 erpnext/patches/v5_0/rename_pos_setting.py delete mode 100644 erpnext/patches/v5_0/rename_table_fieldnames.py delete mode 100644 erpnext/patches/v5_0/rename_taxes_and_charges_master.py delete mode 100644 erpnext/patches/v5_0/rename_total_fields.py delete mode 100644 erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py delete mode 100644 erpnext/patches/v5_0/repost_gle_for_jv_with_multiple_party.py delete mode 100644 erpnext/patches/v5_0/repost_requested_qty.py delete mode 100644 erpnext/patches/v5_0/reset_values_in_tools.py delete mode 100644 erpnext/patches/v5_0/set_appraisal_remarks.py delete mode 100644 erpnext/patches/v5_0/set_default_company_in_bom.py delete mode 100644 erpnext/patches/v5_0/set_footer_address.py delete mode 100644 erpnext/patches/v5_0/stock_entry_update_value.py delete mode 100644 erpnext/patches/v5_0/taxes_and_totals_in_party_currency.py delete mode 100644 erpnext/patches/v5_0/update_account_types.py delete mode 100644 erpnext/patches/v5_0/update_advance_paid.py delete mode 100644 erpnext/patches/v5_0/update_companywise_payment_account.py delete mode 100644 erpnext/patches/v5_0/update_dn_against_doc_fields.py delete mode 100644 erpnext/patches/v5_0/update_from_bom.py delete mode 100644 erpnext/patches/v5_0/update_frozen_accounts_permission_role.py delete mode 100644 erpnext/patches/v5_0/update_item_and_description_again.py delete mode 100644 erpnext/patches/v5_0/update_item_desc_in_invoice.py delete mode 100644 erpnext/patches/v5_0/update_item_description_and_image.py delete mode 100644 erpnext/patches/v5_0/update_item_name_in_bom.py delete mode 100644 erpnext/patches/v5_0/update_journal_entry_title.py delete mode 100644 erpnext/patches/v5_0/update_material_transfer_for_manufacture.py delete mode 100644 erpnext/patches/v5_0/update_material_transferred_for_manufacturing.py delete mode 100644 erpnext/patches/v5_0/update_material_transferred_for_manufacturing_again.py delete mode 100644 erpnext/patches/v5_0/update_operation_description.py delete mode 100644 erpnext/patches/v5_0/update_opportunity.py delete mode 100644 erpnext/patches/v5_0/update_projects.py delete mode 100644 erpnext/patches/v5_0/update_sms_sender.py delete mode 100644 erpnext/patches/v5_0/update_tax_amount_after_discount_in_purchase_cycle.py delete mode 100644 erpnext/patches/v5_0/update_temporary_account.py delete mode 100644 erpnext/patches/v5_0/update_time_log_title.py delete mode 100644 erpnext/patches/v5_1/__init__.py delete mode 100644 erpnext/patches/v5_1/default_bom.py delete mode 100644 erpnext/patches/v5_1/fix_against_account.py delete mode 100644 erpnext/patches/v5_1/rename_roles.py delete mode 100644 erpnext/patches/v5_1/sales_bom_rename.py delete mode 100644 erpnext/patches/v5_2/__init__.py delete mode 100644 erpnext/patches/v5_2/change_item_selects_to_checks.py delete mode 100644 erpnext/patches/v5_4/__init__.py delete mode 100644 erpnext/patches/v5_4/cleanup_journal_entry.py delete mode 100644 erpnext/patches/v5_4/fix_invoice_outstanding.py delete mode 100644 erpnext/patches/v5_4/fix_missing_item_images.py delete mode 100644 erpnext/patches/v5_4/fix_reserved_qty_and_sle_for_packed_items.py delete mode 100644 erpnext/patches/v5_4/notify_system_managers_regarding_wrong_tax_calculation.py delete mode 100644 erpnext/patches/v5_4/set_root_and_report_type.py delete mode 100644 erpnext/patches/v5_4/stock_entry_additional_costs.py delete mode 100644 erpnext/patches/v5_4/update_purchase_cost_against_project.py delete mode 100644 erpnext/patches/v5_7/__init__.py delete mode 100644 erpnext/patches/v5_7/item_template_attributes.py delete mode 100644 erpnext/patches/v5_8/__init__.py delete mode 100644 erpnext/patches/v5_8/add_credit_note_print_heading.py delete mode 100644 erpnext/patches/v5_8/tax_rule.py delete mode 100644 erpnext/patches/v5_8/update_order_reference_in_return_entries.py delete mode 100644 erpnext/patches/v6_0/__init__.py delete mode 100644 erpnext/patches/v6_0/default_activity_rate.py delete mode 100644 erpnext/patches/v6_0/fix_outstanding_amount.py delete mode 100644 erpnext/patches/v6_0/fix_planned_qty.py delete mode 100644 erpnext/patches/v6_0/multi_currency.py delete mode 100644 erpnext/patches/v6_0/set_default_title.py delete mode 100644 erpnext/patches/v6_10/__init__.py delete mode 100644 erpnext/patches/v6_10/email_digest_default_quote.py delete mode 100644 erpnext/patches/v6_10/fix_billed_amount_in_drop_ship_po.py delete mode 100644 erpnext/patches/v6_10/fix_delivery_status_of_drop_ship_item.py delete mode 100644 erpnext/patches/v6_10/fix_jv_total_amount.py delete mode 100644 erpnext/patches/v6_10/fix_ordered_received_billed.py delete mode 100644 erpnext/patches/v6_12/__init__.py delete mode 100644 erpnext/patches/v6_12/repost_entries_with_target_warehouse.py delete mode 100644 erpnext/patches/v6_12/set_overdue_tasks.py delete mode 100644 erpnext/patches/v6_16/__init__.py delete mode 100644 erpnext/patches/v6_16/create_manufacturer_records.py delete mode 100644 erpnext/patches/v6_16/update_billing_status_in_dn_and_pr.py delete mode 100644 erpnext/patches/v6_19/__init__.py delete mode 100644 erpnext/patches/v6_19/comment_feed_communication.py delete mode 100644 erpnext/patches/v6_2/__init__.py delete mode 100644 erpnext/patches/v6_2/fix_missing_default_taxes_and_lead.py delete mode 100644 erpnext/patches/v6_2/remove_newsletter_duplicates.py delete mode 100644 erpnext/patches/v6_20/__init__.py delete mode 100644 erpnext/patches/v6_20/set_party_account_currency_in_orders.py delete mode 100644 erpnext/patches/v6_20x/__init__.py delete mode 100644 erpnext/patches/v6_20x/remove_customer_supplier_roles.py delete mode 100644 erpnext/patches/v6_20x/remove_fiscal_year_from_holiday_list.py delete mode 100644 erpnext/patches/v6_20x/rename_project_name_to_project.py delete mode 100644 erpnext/patches/v6_20x/repost_valuation_rate_for_negative_inventory.py delete mode 100644 erpnext/patches/v6_20x/set_compact_print.py delete mode 100644 erpnext/patches/v6_20x/update_product_bundle_description.py delete mode 100644 erpnext/patches/v6_21/__init__.py delete mode 100644 erpnext/patches/v6_21/fix_reorder_level.py delete mode 100644 erpnext/patches/v6_21/rename_material_request_fields.py delete mode 100644 erpnext/patches/v6_23/__init__.py delete mode 100644 erpnext/patches/v6_23/update_stopped_status_to_closed.py delete mode 100644 erpnext/patches/v6_24/__init__.py delete mode 100644 erpnext/patches/v6_24/map_customer_address_to_shipping_address_on_po.py delete mode 100644 erpnext/patches/v6_24/set_recurring_id.py delete mode 100644 erpnext/patches/v6_27/__init__.py delete mode 100644 erpnext/patches/v6_27/fix_recurring_order_status.py delete mode 100644 erpnext/patches/v6_3/__init__.py delete mode 100644 erpnext/patches/v6_3/convert_applicable_territory.py delete mode 100644 erpnext/patches/v6_4/__init__.py delete mode 100644 erpnext/patches/v6_4/email_digest_update.py delete mode 100644 erpnext/patches/v6_4/fix_duplicate_bins.py delete mode 100644 erpnext/patches/v6_4/fix_expense_included_in_valuation.py delete mode 100644 erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py delete mode 100644 erpnext/patches/v6_4/fix_modified_in_sales_order_and_purchase_order.py delete mode 100644 erpnext/patches/v6_4/fix_sales_order_maintenance_status.py delete mode 100644 erpnext/patches/v6_4/fix_status_in_sales_and_purchase_order.py delete mode 100644 erpnext/patches/v6_4/make_image_thumbnail.py delete mode 100644 erpnext/patches/v6_4/repost_gle_for_journal_entries_where_reference_name_missing.py delete mode 100644 erpnext/patches/v6_4/round_status_updater_percentages.py delete mode 100644 erpnext/patches/v6_4/set_user_in_contact.py delete mode 100644 erpnext/patches/v6_5/__init__.py delete mode 100644 erpnext/patches/v6_5/show_in_website_for_template_item.py delete mode 100644 erpnext/patches/v6_6/__init__.py delete mode 100644 erpnext/patches/v6_6/fix_website_image.py delete mode 100644 erpnext/patches/v6_6/remove_fiscal_year_from_leave_allocation.py delete mode 100644 erpnext/patches/v6_8/__init__.py delete mode 100644 erpnext/patches/v6_8/make_webform_standard.py delete mode 100644 erpnext/patches/v6_8/move_drop_ship_to_po_items.py delete mode 100644 erpnext/patches/v7_0/__init__.py delete mode 100644 erpnext/patches/v7_0/calculate_total_costing_amount.py delete mode 100644 erpnext/patches/v7_0/convert_timelog_to_timesheet.py delete mode 100644 erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py delete mode 100644 erpnext/patches/v7_0/create_budget_record.py delete mode 100644 erpnext/patches/v7_0/create_warehouse_nestedset.py delete mode 100644 erpnext/patches/v7_0/fix_duplicate_icons.py delete mode 100644 erpnext/patches/v7_0/fix_nonwarehouse_ledger_gl_entries_for_transactions.py delete mode 100644 erpnext/patches/v7_0/make_guardian.py delete mode 100644 erpnext/patches/v7_0/make_is_group_fieldtype_as_check.py delete mode 100644 erpnext/patches/v7_0/merge_account_type_stock_and_warehouse_to_stock.py delete mode 100644 erpnext/patches/v7_0/migrate_mode_of_payments_v6_to_v7.py delete mode 100644 erpnext/patches/v7_0/migrate_schools_to_erpnext.py delete mode 100644 erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py delete mode 100644 erpnext/patches/v7_0/po_status_issue_for_pr_return.py delete mode 100644 erpnext/patches/v7_0/re_route.py delete mode 100644 erpnext/patches/v7_0/remove_administrator_role_in_doctypes.py delete mode 100644 erpnext/patches/v7_0/remove_doctypes_and_reports.py delete mode 100644 erpnext/patches/v7_0/remove_features_setup.py delete mode 100644 erpnext/patches/v7_0/remove_old_earning_deduction_doctypes.py delete mode 100644 erpnext/patches/v7_0/rename_advance_table_fields.py delete mode 100644 erpnext/patches/v7_0/rename_examination_to_assessment.py delete mode 100644 erpnext/patches/v7_0/rename_fee_amount_to_fee_component.py delete mode 100644 erpnext/patches/v7_0/rename_prevdoc_fields.py delete mode 100644 erpnext/patches/v7_0/rename_salary_components.py delete mode 100644 erpnext/patches/v7_0/rename_time_sheet_doctype.py delete mode 100644 erpnext/patches/v7_0/repost_bin_qty_and_item_projected_qty.py delete mode 100644 erpnext/patches/v7_0/repost_gle_for_pi_with_update_stock.py delete mode 100644 erpnext/patches/v7_0/repost_gle_for_pos_sales_return.py delete mode 100644 erpnext/patches/v7_0/set_base_amount_in_invoice_payment_table.py delete mode 100644 erpnext/patches/v7_0/set_is_group_for_warehouse.py delete mode 100644 erpnext/patches/v7_0/set_material_request_type_in_item.py delete mode 100644 erpnext/patches/v7_0/set_naming_series_for_timesheet.py delete mode 100644 erpnext/patches/v7_0/set_party_name_in_payment_entry.py delete mode 100644 erpnext/patches/v7_0/set_portal_settings.py delete mode 100644 erpnext/patches/v7_0/setup_account_table_for_expense_claim_type_if_exists.py delete mode 100644 erpnext/patches/v7_0/system_settings_setup_complete.py delete mode 100644 erpnext/patches/v7_0/update_autoname_field.py delete mode 100644 erpnext/patches/v7_0/update_change_amount_account.py delete mode 100644 erpnext/patches/v7_0/update_conversion_factor_in_supplier_quotation_item.py delete mode 100644 erpnext/patches/v7_0/update_home_page.py delete mode 100644 erpnext/patches/v7_0/update_maintenance_module_in_doctype.py delete mode 100644 erpnext/patches/v7_0/update_mins_to_first_response.py delete mode 100644 erpnext/patches/v7_0/update_missing_employee_in_timesheet.py delete mode 100644 erpnext/patches/v7_0/update_mode_of_payment_type.py delete mode 100644 erpnext/patches/v7_0/update_party_status.py delete mode 100644 erpnext/patches/v7_0/update_prevdoc_values_for_supplier_quotation_item.py delete mode 100644 erpnext/patches/v7_0/update_project_in_gl_entry.py delete mode 100644 erpnext/patches/v7_0/update_refdoc_in_landed_cost_voucher.py delete mode 100644 erpnext/patches/v7_0/update_status_for_timesheet.py delete mode 100644 erpnext/patches/v7_0/update_status_of_po_so.py delete mode 100644 erpnext/patches/v7_0/update_status_of_zero_amount_sales_order.py delete mode 100644 erpnext/patches/v7_0/update_timesheet_communications.py delete mode 100644 erpnext/patches/v7_1/__init__.py delete mode 100644 erpnext/patches/v7_1/add_account_user_role_for_timesheet.py delete mode 100644 erpnext/patches/v7_1/add_field_for_task_dependent.py delete mode 100644 erpnext/patches/v7_1/fix_link_for_customer_from_lead.py delete mode 100644 erpnext/patches/v7_1/move_sales_invoice_from_parent_to_child_timesheet.py delete mode 100644 erpnext/patches/v7_1/rename_field_timesheet.py delete mode 100644 erpnext/patches/v7_1/rename_quality_inspection_field.py delete mode 100644 erpnext/patches/v7_1/repost_stock_for_deleted_bins_for_merging_items.py delete mode 100644 erpnext/patches/v7_1/save_stock_settings.py delete mode 100644 erpnext/patches/v7_1/set_budget_against_as_cost_center.py delete mode 100644 erpnext/patches/v7_1/set_currency_exchange_date.py delete mode 100644 erpnext/patches/v7_1/set_prefered_contact_email.py delete mode 100644 erpnext/patches/v7_1/set_sales_person_status.py delete mode 100644 erpnext/patches/v7_1/set_student_guardian.py delete mode 100644 erpnext/patches/v7_1/set_total_amount_currency_in_je.py delete mode 100644 erpnext/patches/v7_1/update_bom_base_currency.py delete mode 100644 erpnext/patches/v7_1/update_component_type.py delete mode 100644 erpnext/patches/v7_1/update_invoice_status.py delete mode 100644 erpnext/patches/v7_1/update_lead_source.py delete mode 100644 erpnext/patches/v7_1/update_missing_salary_component_type.py delete mode 100644 erpnext/patches/v7_1/update_portal_roles.py delete mode 100644 erpnext/patches/v7_1/update_total_billing_hours.py delete mode 100644 erpnext/patches/v7_2/__init__.py delete mode 100644 erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py delete mode 100644 erpnext/patches/v7_2/contact_address_links.py delete mode 100644 erpnext/patches/v7_2/delete_fleet_management_module_def.py delete mode 100644 erpnext/patches/v7_2/empty_supplied_items_for_non_subcontracted.py delete mode 100644 erpnext/patches/v7_2/make_all_assessment_group.py delete mode 100644 erpnext/patches/v7_2/mark_students_active.py delete mode 100644 erpnext/patches/v7_2/rename_att_date_attendance.py delete mode 100644 erpnext/patches/v7_2/rename_evaluation_criteria.py delete mode 100644 erpnext/patches/v7_2/set_null_value_to_fields.py delete mode 100644 erpnext/patches/v7_2/setup_auto_close_settings.py delete mode 100644 erpnext/patches/v7_2/stock_uom_in_selling.py delete mode 100644 erpnext/patches/v7_2/update_abbr_in_salary_slips.py delete mode 100644 erpnext/patches/v7_2/update_assessment_modules.py delete mode 100644 erpnext/patches/v7_2/update_attendance_docstatus.py delete mode 100644 erpnext/patches/v7_2/update_doctype_status.py delete mode 100644 erpnext/patches/v7_2/update_guardian_name_in_student_master.py delete mode 100644 erpnext/patches/v7_2/update_party_type.py delete mode 100644 erpnext/patches/v7_2/update_salary_slips.py delete mode 100644 erpnext/patches/v7_2/update_website_for_variant.py delete mode 100644 erpnext/patches/v8_0/__init__.py delete mode 100644 erpnext/patches/v8_0/addresses_linked_to_lead.py delete mode 100644 erpnext/patches/v8_0/change_in_words_varchar_length.py delete mode 100644 erpnext/patches/v8_0/create_address_doc_from_address_field_in_company.py delete mode 100644 erpnext/patches/v8_0/create_domain_docs.py delete mode 100644 erpnext/patches/v8_0/delete_bin_indexes.py delete mode 100644 erpnext/patches/v8_0/delete_schools_depricated_doctypes.py delete mode 100644 erpnext/patches/v8_0/disable_instructor_role.py delete mode 100644 erpnext/patches/v8_0/enable_booking_asset_depreciation_automatically.py delete mode 100644 erpnext/patches/v8_0/fix_status_for_invoices_with_negative_outstanding.py delete mode 100644 erpnext/patches/v8_0/make_payments_table_blank_for_non_pos_invoice.py delete mode 100644 erpnext/patches/v8_0/merge_student_batch_and_student_group.py delete mode 100644 erpnext/patches/v8_0/move_account_head_from_account_to_warehouse_for_inventory.py delete mode 100644 erpnext/patches/v8_0/move_perpetual_inventory_setting.py delete mode 100644 erpnext/patches/v8_0/rename_is_sample_item_to_allow_zero_valuation_rate.py delete mode 100644 erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py delete mode 100644 erpnext/patches/v8_0/rename_total_margin_to_rate_with_margin.py delete mode 100644 erpnext/patches/v8_0/repost_reserved_qty_for_multiple_sales_uom.py delete mode 100644 erpnext/patches/v8_0/revert_manufacturers_table_from_item.py delete mode 100644 erpnext/patches/v8_0/save_system_settings.py delete mode 100644 erpnext/patches/v8_0/set_null_to_serial_nos_for_disabled_sales_invoices.py delete mode 100644 erpnext/patches/v8_0/set_project_copied_from.py delete mode 100644 erpnext/patches/v8_0/set_sales_invoice_serial_number_from_delivery_note.py delete mode 100644 erpnext/patches/v8_0/update_customer_pos_id.py delete mode 100644 erpnext/patches/v8_0/update_production_orders.py delete mode 100644 erpnext/patches/v8_0/update_sales_cost_in_project.py delete mode 100644 erpnext/patches/v8_0/update_status_as_paid_for_completed_expense_claim.py delete mode 100644 erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py delete mode 100644 erpnext/patches/v8_0/update_stock_qty_value_in_purchase_invoice.py delete mode 100644 erpnext/patches/v8_0/update_student_groups_from_student_batches.py delete mode 100644 erpnext/patches/v8_0/update_supplier_address_in_stock_entry.py delete mode 100644 erpnext/patches/v8_1/__init__.py delete mode 100644 erpnext/patches/v8_1/add_hsn_sac_codes.py delete mode 100644 erpnext/patches/v8_1/add_indexes_in_transaction_doctypes.py delete mode 100644 erpnext/patches/v8_1/allow_invoice_copy_to_edit_after_submit.py delete mode 100644 erpnext/patches/v8_1/delete_deprecated_reports.py delete mode 100644 erpnext/patches/v8_1/gst_fixes.py delete mode 100644 erpnext/patches/v8_1/remove_sales_invoice_from_returned_serial_no.py delete mode 100644 erpnext/patches/v8_1/removed_report_support_hours.py delete mode 100644 erpnext/patches/v8_1/set_delivery_date_in_so_item.py delete mode 100644 erpnext/patches/v8_1/update_expense_claim_status.py delete mode 100644 erpnext/patches/v8_1/update_gst_state.py delete mode 100644 erpnext/patches/v8_10/__init__.py delete mode 100644 erpnext/patches/v8_10/change_default_customer_credit_days.py delete mode 100644 erpnext/patches/v8_3/__init__.py delete mode 100644 erpnext/patches/v8_3/set_restrict_to_domain_for_module_def.py delete mode 100644 erpnext/patches/v8_3/update_company_total_sales.py delete mode 100644 erpnext/patches/v8_4/__init__.py delete mode 100644 erpnext/patches/v8_4/make_scorecard_records.py delete mode 100644 erpnext/patches/v8_5/__init__.py delete mode 100644 erpnext/patches/v8_5/fix_tax_breakup_for_non_invoice_docs.py delete mode 100644 erpnext/patches/v8_5/remove_project_type_property_setter.py delete mode 100644 erpnext/patches/v8_5/remove_quotations_route_in_sidebar.py delete mode 100644 erpnext/patches/v8_5/set_default_mode_of_payment.py delete mode 100644 erpnext/patches/v8_5/update_customer_group_in_POS_profile.py delete mode 100644 erpnext/patches/v8_5/update_existing_data_in_project_type.py delete mode 100644 erpnext/patches/v8_6/__init__.py delete mode 100644 erpnext/patches/v8_6/point_sms_doctype_module_to_frappe_core.py delete mode 100644 erpnext/patches/v8_6/rename_bom_update_tool.py delete mode 100644 erpnext/patches/v8_6/set_write_permission_for_quotation_for_sales_manager.py delete mode 100644 erpnext/patches/v8_6/update_timesheet_company_from_PO.py delete mode 100644 erpnext/patches/v8_7/__init__.py delete mode 100644 erpnext/patches/v8_7/fix_purchase_receipt_status.py delete mode 100644 erpnext/patches/v8_7/make_subscription_from_recurring_data.py delete mode 100644 erpnext/patches/v8_8/__init__.py delete mode 100644 erpnext/patches/v8_8/add_new_fields_in_accounts_settings.py delete mode 100644 erpnext/patches/v8_8/set_bom_rate_as_per_uom.py delete mode 100644 erpnext/patches/v8_9/__init__.py delete mode 100644 erpnext/patches/v8_9/add_setup_progress_actions.py delete mode 100644 erpnext/patches/v8_9/delete_gst_doctypes_for_outside_india_accounts.py delete mode 100644 erpnext/patches/v8_9/remove_employee_from_salary_structure_parent.py delete mode 100644 erpnext/patches/v8_9/rename_company_sales_target_field.py delete mode 100644 erpnext/patches/v8_9/set_default_customer_group.py delete mode 100644 erpnext/patches/v8_9/set_default_fields_in_variant_settings.py delete mode 100644 erpnext/patches/v8_9/set_member_party_type.py delete mode 100644 erpnext/patches/v8_9/set_print_zero_amount_taxes.py delete mode 100644 erpnext/patches/v8_9/update_billing_gstin_for_indian_account.py delete mode 100644 erpnext/patches/v9_0/__init__.py delete mode 100644 erpnext/patches/v9_0/add_healthcare_domain.py delete mode 100644 erpnext/patches/v9_0/add_user_to_child_table_in_pos_profile.py delete mode 100644 erpnext/patches/v9_0/copy_old_fees_field_data.py delete mode 100644 erpnext/patches/v9_0/remove_non_existing_warehouse_from_stock_settings.py delete mode 100644 erpnext/patches/v9_0/remove_subscription_module.py delete mode 100644 erpnext/patches/v9_0/revert_manufacturing_user_role.py delete mode 100644 erpnext/patches/v9_0/set_pos_profile_name.py delete mode 100644 erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py delete mode 100644 erpnext/patches/v9_0/set_shipping_type_for_existing_shipping_rules.py delete mode 100644 erpnext/patches/v9_0/set_uoms_in_variant_field.py delete mode 100644 erpnext/patches/v9_0/set_variant_item_description.py delete mode 100644 erpnext/patches/v9_0/student_admission_childtable_migrate.py delete mode 100644 erpnext/patches/v9_0/update_employee_loan_details.py delete mode 100644 erpnext/patches/v9_0/update_multi_uom_fields_in_material_request.py delete mode 100644 erpnext/patches/v9_1/__init__.py delete mode 100644 erpnext/patches/v9_1/create_issue_opportunity_type.py delete mode 100644 erpnext/patches/v9_2/__init__.py delete mode 100644 erpnext/patches/v9_2/delete_healthcare_domain_default_items.py delete mode 100644 erpnext/patches/v9_2/delete_process_payroll.py delete mode 100644 erpnext/patches/v9_2/remove_company_from_patient.py delete mode 100644 erpnext/patches/v9_2/rename_net_weight_in_item_master.py delete mode 100644 erpnext/patches/v9_2/rename_translated_domains_in_en.py delete mode 100644 erpnext/patches/v9_2/repost_reserved_qty_for_production.py delete mode 100644 erpnext/patches/v9_2/set_item_name_in_production_order.py diff --git a/erpnext/patches/repair_tools/__init__.py b/erpnext/patches/repair_tools/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/repair_tools/set_stock_balance_as_per_serial_no.py b/erpnext/patches/repair_tools/set_stock_balance_as_per_serial_no.py deleted file mode 100644 index 5a421d146f..0000000000 --- a/erpnext/patches/repair_tools/set_stock_balance_as_per_serial_no.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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 - -def execute(): - from erpnext.stock.stock_balance import set_stock_balance_as_per_serial_no - frappe.db.auto_commit_on_many_writes = 1 - - set_stock_balance_as_per_serial_no() - - frappe.db.auto_commit_on_many_writes = 0 diff --git a/erpnext/patches/v10_0/__init__.py b/erpnext/patches/v10_0/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v10_0/add_agriculture_domain.py b/erpnext/patches/v10_0/add_agriculture_domain.py deleted file mode 100644 index c18e69f3e6..0000000000 --- a/erpnext/patches/v10_0/add_agriculture_domain.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - domain = 'Agriculture' - if not frappe.db.exists('Domain', domain): - frappe.get_doc({ - 'doctype': 'Domain', - 'domain': domain - }).insert(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/patches/v10_0/add_guardian_role_for_parent_portal.py b/erpnext/patches/v10_0/add_guardian_role_for_parent_portal.py deleted file mode 100644 index 0b891f21f4..0000000000 --- a/erpnext/patches/v10_0/add_guardian_role_for_parent_portal.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - # create guardian role - if not frappe.get_value('Role', dict(role_name='Guardian')): - frappe.get_doc({ - 'doctype': 'Role', - 'role_name': 'Guardian', - 'desk_access': 0, - 'restrict_to_domain': 'Education' - }).insert(ignore_permissions=True) - - # set guardian roles in already created users - if frappe.db.exists("Doctype", "Guardian"): - for user in frappe.db.sql_list("""select u.name from `tabUser` u , `tabGuardian` g where g.email_address = u.name"""): - user = frappe.get_doc('User', user) - user.flags.ignore_validate = True - user.flags.ignore_mandatory = True - user.save() diff --git a/erpnext/patches/v10_0/add_non_profit_domain.py b/erpnext/patches/v10_0/add_non_profit_domain.py deleted file mode 100644 index b03d669515..0000000000 --- a/erpnext/patches/v10_0/add_non_profit_domain.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - domain = 'Non Profit' - if not frappe.db.exists('Domain', domain): - frappe.get_doc({ - 'doctype': 'Domain', - 'domain': domain - }).insert(ignore_permissions=True) - - frappe.get_doc({ - 'doctype': 'Role', - 'role_name': 'Non Profit Portal User', - 'desk_access': 0, - 'restrict_to_domain': domain - }).insert(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/patches/v10_0/allow_operators_in_supplier_scorecard.py b/erpnext/patches/v10_0/allow_operators_in_supplier_scorecard.py deleted file mode 100644 index 827f9bc94f..0000000000 --- a/erpnext/patches/v10_0/allow_operators_in_supplier_scorecard.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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('buying', 'doctype', 'supplier_scorecard_criteria') - frappe.reload_doc('buying', 'doctype', 'supplier_scorecard_scoring_criteria') - frappe.reload_doc('buying', 'doctype', 'supplier_scorecard') - - for criteria in frappe.get_all('Supplier Scorecard Criteria', fields=['name', 'formula'], limit_page_length=None): - frappe.db.set_value('Supplier Scorecard Criteria', criteria.name, - 'formula', criteria.formula.replace('<','<').replace('>','>')) - - for criteria in frappe.get_all('Supplier Scorecard Scoring Criteria', fields=['name', 'formula'], limit_page_length=None): - if criteria.formula: # not mandatory - frappe.db.set_value('Supplier Scorecard Scoring Criteria', criteria.name, - 'formula', criteria.formula.replace('<','<').replace('>','>')) - - for sc in frappe.get_all('Supplier Scorecard', fields=['name', 'weighting_function'], limit_page_length=None): - frappe.db.set_value('Supplier Scorecard', sc.name, 'weighting_function', - sc.weighting_function.replace('<','<').replace('>','>')) \ No newline at end of file diff --git a/erpnext/patches/v10_0/copy_projects_renamed_fields.py b/erpnext/patches/v10_0/copy_projects_renamed_fields.py deleted file mode 100644 index 80db3bdd1e..0000000000 --- a/erpnext/patches/v10_0/copy_projects_renamed_fields.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import rename_field - -def execute(): - """ copy data from old fields to new """ - frappe.reload_doc("projects", "doctype", "project") - - if frappe.db.has_column('Project', 'total_sales_cost'): - rename_field('Project', "total_sales_cost", "total_sales_amount") - - if frappe.db.has_column('Project', 'total_billing_amount'): - rename_field('Project', "total_billing_amount", "total_billable_amount") \ No newline at end of file diff --git a/erpnext/patches/v10_0/enabled_regional_print_format_based_on_country.py b/erpnext/patches/v10_0/enabled_regional_print_format_based_on_country.py deleted file mode 100644 index 38b04cebc2..0000000000 --- a/erpnext/patches/v10_0/enabled_regional_print_format_based_on_country.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - print_format_mapper = { - 'India': ['GST POS Invoice', 'GST Tax Invoice'], - 'Saudi Arabia': ['Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice'], - 'United Arab Emirates': ['Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice'] - } - - frappe.db.sql(""" update `tabPrint Format` set disabled = 1 where name - in ('GST POS Invoice', 'GST Tax Invoice', 'Simplified Tax Invoice', 'Detailed Tax Invoice')""") - - for d in frappe.get_all('Company', fields = ["country"], - filters={'country': ('in', ['India', 'Saudi Arabia', 'United Arab Emirates'])}): - if print_format_mapper.get(d.country): - print_formats = print_format_mapper.get(d.country) - frappe.db.sql(""" update `tabPrint Format` set disabled = 0 - where name in (%s)""" % ", ".join(["%s"]*len(print_formats)), tuple(print_formats)) \ No newline at end of file diff --git a/erpnext/patches/v10_0/fix_reserved_qty_for_sub_contract.py b/erpnext/patches/v10_0/fix_reserved_qty_for_sub_contract.py deleted file mode 100644 index c0a9e5eb5b..0000000000 --- a/erpnext/patches/v10_0/fix_reserved_qty_for_sub_contract.py +++ /dev/null @@ -1,31 +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 erpnext.stock.utils import get_bin - -def execute(): - frappe.reload_doc("stock", "doctype", "bin") - frappe.reload_doc("buying", "doctype", "purchase_order_item_supplied") - for d in frappe.db.sql(""" - select distinct rm_item_code, reserve_warehouse - from `tabPurchase Order Item Supplied` - where docstatus=1 and reserve_warehouse is not null and reserve_warehouse != ''"""): - - try: - bin_doc = get_bin(d[0], d[1]) - bin_doc.update_reserved_qty_for_sub_contracting() - except: - pass - - for d in frappe.db.sql("""select distinct item_code, source_warehouse - from `tabWork Order Item` - where docstatus=1 and transferred_qty > required_qty - and source_warehouse is not null and source_warehouse != ''""", as_list=1): - - try: - bin_doc = get_bin(d[0], d[1]) - bin_doc.update_reserved_qty_for_production() - except: - pass diff --git a/erpnext/patches/v10_0/recalculate_gross_margin_for_project.py b/erpnext/patches/v10_0/recalculate_gross_margin_for_project.py deleted file mode 100644 index 6d461f3bc9..0000000000 --- a/erpnext/patches/v10_0/recalculate_gross_margin_for_project.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('projects', 'doctype', 'project') - for d in frappe.db.sql(""" select name from `tabProject` where - ifnull(total_consumed_material_cost, 0 ) > 0 and ifnull(total_billed_amount, 0) > 0""", as_dict=1): - doc = frappe.get_doc("Project", d.name) - doc.calculate_gross_margin() - doc.db_set('gross_margin', doc.gross_margin) - doc.db_set('per_gross_margin', doc.per_gross_margin) \ No newline at end of file diff --git a/erpnext/patches/v10_0/rename_schools_to_education.py b/erpnext/patches/v10_0/rename_schools_to_education.py deleted file mode 100644 index 85c25a8943..0000000000 --- a/erpnext/patches/v10_0/rename_schools_to_education.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - # rename the School module as Education - - # rename the school module - if frappe.db.exists('Module Def', 'Schools') and not frappe.db.exists('Module Def', 'Education'): - frappe.rename_doc("Module Def", "Schools", "Education") - - # delete the school module - if frappe.db.exists('Module Def', 'Schools') and frappe.db.exists('Module Def', 'Education'): - frappe.db.sql("""delete from `tabModule Def` where module_name = 'Schools'""") - - - # rename "School Settings" to the "Education Settings - if frappe.db.exists('DocType', 'School Settings'): - frappe.rename_doc("DocType", "School Settings", "Education Settings", force=True) - frappe.reload_doc("education", "doctype", "education_settings") - - # delete the discussion web form if exists - if frappe.db.exists('Web Form', 'Discussion'): - frappe.db.sql("""delete from `tabWeb Form` where name = 'discussion'""") - - # rename the select option field from "School Bus" to "Institute's Bus" - frappe.reload_doc("education", "doctype", "Program Enrollment") - if "mode_of_transportation" in frappe.db.get_table_columns("Program Enrollment"): - frappe.db.sql("""update `tabProgram Enrollment` set mode_of_transportation = "Institute's Bus" - where mode_of_transportation = "School Bus" """) diff --git a/erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py b/erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py deleted file mode 100644 index e6546e386b..0000000000 --- a/erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe, erpnext - -def execute(): - for company in frappe.get_all("Company"): - if not erpnext.is_perpetual_inventory_enabled(company.name): - continue - - acc_frozen_upto = frappe.db.get_value("Accounts Settings", None, "acc_frozen_upto") or "1900-01-01" - pr_with_rejected_warehouse = frappe.db.sql(""" - select pr.name - from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item - where pr.name = pr_item.parent - and pr.posting_date > %s - and pr.docstatus=1 - and pr.company = %s - and pr_item.rejected_qty > 0 - """, (acc_frozen_upto, company.name), as_dict=1) - - for d in pr_with_rejected_warehouse: - doc = frappe.get_doc("Purchase Receipt", d.name) - - doc.docstatus = 2 - doc.make_gl_entries_on_cancel() - - - # update gl entries for submit state of PR - doc.docstatus = 1 - doc.make_gl_entries() diff --git a/erpnext/patches/v10_0/repost_requested_qty_for_non_stock_uom_items.py b/erpnext/patches/v10_0/repost_requested_qty_for_non_stock_uom_items.py deleted file mode 100644 index 4fe4e97cf5..0000000000 --- a/erpnext/patches/v10_0/repost_requested_qty_for_non_stock_uom_items.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2019, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty - - count=0 - for item_code, warehouse in frappe.db.sql("""select distinct item_code, warehouse - from `tabMaterial Request Item` where docstatus = 1 and stock_uom<>uom"""): - try: - count += 1 - update_bin_qty(item_code, warehouse, { - "indented_qty": get_indented_qty(item_code, warehouse), - }) - if count % 200 == 0: - frappe.db.commit() - except: - frappe.db.rollback() diff --git a/erpnext/patches/v10_0/set_auto_created_serial_no_in_stock_entry.py b/erpnext/patches/v10_0/set_auto_created_serial_no_in_stock_entry.py deleted file mode 100644 index c6470f21d7..0000000000 --- a/erpnext/patches/v10_0/set_auto_created_serial_no_in_stock_entry.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - serialised_items = [d.name for d in frappe.get_all("Item", filters={"has_serial_no": 1})] - - if not serialised_items: - return - - for dt in ["Stock Entry Detail", "Purchase Receipt Item", "Purchase Invoice Item"]: - cond = "" - if dt=="Purchase Invoice Item": - cond = """ and parent in (select name from `tabPurchase Invoice` - where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.parent and update_stock=1)""" - - item_rows = frappe.db.sql(""" - select name - from `tab{0}` - where conversion_factor != 1 - and docstatus = 1 - and ifnull(serial_no, '') = '' - and item_code in ({1}) - {2} - """.format(dt, ', '.join(['%s']*len(serialised_items)), cond), tuple(serialised_items)) - - if item_rows: - sle_serial_nos = dict(frappe.db.sql(""" - select voucher_detail_no, serial_no - from `tabStock Ledger Entry` - where ifnull(serial_no, '') != '' - and voucher_detail_no in (%s) - """.format(', '.join(['%s']*len(item_rows))), - tuple([d[0] for d in item_rows]))) - - batch_size = 100 - for i in range(0, len(item_rows), batch_size): - batch_item_rows = item_rows[i:i + batch_size] - when_then = [] - for item_row in batch_item_rows: - - when_then.append('WHEN `name` = "{row_name}" THEN "{value}"'.format( - row_name=item_row[0], - value=sle_serial_nos.get(item_row[0]))) - - frappe.db.sql(""" - update - `tab{doctype}` - set - serial_no = CASE {when_then_cond} ELSE `serial_no` END - """.format( - doctype = dt, - when_then_cond=" ".join(when_then) - )) \ No newline at end of file diff --git a/erpnext/patches/v10_0/set_b2c_limit.py b/erpnext/patches/v10_0/set_b2c_limit.py deleted file mode 100644 index 5d964e681a..0000000000 --- a/erpnext/patches/v10_0/set_b2c_limit.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("regional", "doctype", "gst_settings") - frappe.reload_doc("accounts", "doctype", "gst_account") - gst_settings = frappe.get_doc("GST Settings") - gst_settings.b2c_limit = 250000 - gst_settings.save() diff --git a/erpnext/patches/v10_0/set_default_payment_terms_based_on_company.py b/erpnext/patches/v10_0/set_default_payment_terms_based_on_company.py deleted file mode 100644 index a90e096390..0000000000 --- a/erpnext/patches/v10_0/set_default_payment_terms_based_on_company.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import unicode_literals -import frappe -from erpnext.patches.v8_10.change_default_customer_credit_days import make_payment_term, make_template - -def execute(): - for dt in ("Company", "Customer Group"): - frappe.reload_doc("setup", "doctype", frappe.scrub(dt)) - - credit_records = frappe.db.sql(""" - SELECT DISTINCT `credit_days`, `credit_days_based_on`, `name` - from `tab{0}` - where - ((credit_days_based_on='Fixed Days' or credit_days_based_on is null) and credit_days is not null) - or credit_days_based_on='Last Day of the Next Month' - """.format(dt), as_dict=1) - - for d in credit_records: - template = create_payment_terms_template(d) - - frappe.db.sql(""" - update `tab{0}` - set `payment_terms` = %s - where name = %s - """.format(dt), (template.name, d.name)) - -def create_payment_terms_template(data): - if data.credit_days_based_on == "Fixed Days": - pyt_template_name = 'Default Payment Term - N{0}'.format(data.credit_days) - else: - pyt_template_name = 'Default Payment Term - EO2M' - - if not frappe.db.exists("Payment Terms Template", pyt_template_name): - payment_term = make_payment_term(data.credit_days, data.credit_days_based_on) - template = make_template(payment_term) - else: - template = frappe.get_doc("Payment Terms Template", pyt_template_name) - return template \ No newline at end of file diff --git a/erpnext/patches/v10_0/set_discount_amount.py b/erpnext/patches/v10_0/set_discount_amount.py deleted file mode 100644 index d5e2c5a84b..0000000000 --- a/erpnext/patches/v10_0/set_discount_amount.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import unicode_literals - -import frappe - -def execute(): - frappe.reload_doc("accounts", "doctype", "sales_invoice_item") - frappe.reload_doc('accounts', 'doctype', 'purchase_invoice_item') - frappe.reload_doc('buying', 'doctype', 'purchase_order_item') - frappe.reload_doc('buying', 'doctype', 'supplier_quotation_item') - frappe.reload_doc('selling', 'doctype', 'sales_order_item') - frappe.reload_doc('selling', 'doctype', 'quotation_item') - frappe.reload_doc('stock', 'doctype', 'delivery_note_item') - frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item') - - selling_doctypes = ["Sales Order Item", "Sales Invoice Item", "Delivery Note Item", "Quotation Item"] - buying_doctypes = ["Purchase Order Item", "Purchase Invoice Item", "Purchase Receipt Item", "Supplier Quotation Item"] - - for doctype in selling_doctypes: - frappe.db.sql(''' - UPDATE - `tab%s` - SET - discount_amount = if(rate_with_margin > 0, rate_with_margin, price_list_rate) * discount_percentage / 100 - WHERE - discount_percentage > 0 - ''' % (doctype)) - for doctype in buying_doctypes: - frappe.db.sql(''' - UPDATE - `tab%s` - SET - discount_amount = price_list_rate * discount_percentage / 100 - WHERE - discount_percentage > 0 - ''' % (doctype)) \ No newline at end of file diff --git a/erpnext/patches/v10_0/set_numeric_ranges_in_template_if_blank.py b/erpnext/patches/v10_0/set_numeric_ranges_in_template_if_blank.py deleted file mode 100644 index 6825f19d74..0000000000 --- a/erpnext/patches/v10_0/set_numeric_ranges_in_template_if_blank.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - item_numeric_attributes = frappe.db.sql(""" - select name, numeric_values, from_range, to_range, increment - from `tabItem Attribute` - where numeric_values = 1 - """, as_dict=1) - - for d in item_numeric_attributes: - frappe.db.sql(""" - update `tabItem Variant Attribute` - set - from_range = CASE - WHEN from_range = 0 THEN %(from_range)s - ELSE from_range - END, - to_range = CASE - WHEN to_range = 0 THEN %(to_range)s - ELSE to_range - END, - increment = CASE - WHEN increment = 0 THEN %(increment)s - ELSE increment - END, - numeric_values = %(numeric_values)s - where - attribute = %(name)s - and exists(select name from tabItem - where name=`tabItem Variant Attribute`.parent and has_variants=1) - """, d) \ No newline at end of file diff --git a/erpnext/patches/v10_0/set_primary_contact_for_customer.py b/erpnext/patches/v10_0/set_primary_contact_for_customer.py deleted file mode 100644 index ae0b31c21f..0000000000 --- a/erpnext/patches/v10_0/set_primary_contact_for_customer.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype('Customer') - - frappe.db.sql(""" - update - `tabCustomer`, ( - select `tabContact`.name, `tabContact`.mobile_no, `tabContact`.email_id, - `tabDynamic Link`.link_name from `tabContact`, `tabDynamic Link` - where `tabContact`.name = `tabDynamic Link`.parent and - `tabDynamic Link`.link_doctype = 'Customer' and `tabContact`.is_primary_contact = 1 - ) as contact - set - `tabCustomer`.customer_primary_contact = contact.name, - `tabCustomer`.mobile_no = contact.mobile_no, `tabCustomer`.email_id = contact.email_id - where `tabCustomer`.name = contact.link_name""") \ No newline at end of file diff --git a/erpnext/patches/v10_0/set_qty_in_transactions_based_on_serial_no_input.py b/erpnext/patches/v10_0/set_qty_in_transactions_based_on_serial_no_input.py deleted file mode 100644 index 083b7f4b20..0000000000 --- a/erpnext/patches/v10_0/set_qty_in_transactions_based_on_serial_no_input.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2017, 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", "stock_settings") - - ss = frappe.get_doc("Stock Settings") - ss.set_qty_in_transactions_based_on_serial_no_input = 1 - - if ss.default_warehouse \ - and not frappe.db.exists("Warehouse", ss.default_warehouse): - ss.default_warehouse = None - - if ss.stock_uom and not frappe.db.exists("UOM", ss.stock_uom): - ss.stock_uom = None - - ss.flags.ignore_mandatory = True - ss.save() \ No newline at end of file diff --git a/erpnext/patches/v10_0/set_student_party_type.py b/erpnext/patches/v10_0/set_student_party_type.py deleted file mode 100644 index 08376ae894..0000000000 --- a/erpnext/patches/v10_0/set_student_party_type.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if not frappe.db.exists("Party Type", "Student"): - party = frappe.new_doc("Party Type") - party.party_type = "Student" - party.save() diff --git a/erpnext/patches/v10_0/setup_vat_for_uae_and_saudi_arabia.py b/erpnext/patches/v10_0/setup_vat_for_uae_and_saudi_arabia.py deleted file mode 100644 index a8d90499d8..0000000000 --- a/erpnext/patches/v10_0/setup_vat_for_uae_and_saudi_arabia.py +++ /dev/null @@ -1,13 +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 erpnext.setup.doctype.company.company import install_country_fixtures - -def execute(): - frappe.reload_doc("accounts", "doctype", "account") - frappe.reload_doc("accounts", "doctype", "payment_schedule") - for d in frappe.get_all('Company', - filters={'country': ('in', ['Saudi Arabia', 'United Arab Emirates'])}): - install_country_fixtures(d.name) \ No newline at end of file diff --git a/erpnext/patches/v10_0/show_leaves_of_all_department_members_in_calendar.py b/erpnext/patches/v10_0/show_leaves_of_all_department_members_in_calendar.py deleted file mode 100644 index 7e2ff7a8a7..0000000000 --- a/erpnext/patches/v10_0/show_leaves_of_all_department_members_in_calendar.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("hr", "doctype", "hr_settings") - frappe.db.set_value("HR Settings", None, "show_leaves_of_all_department_members_in_calendar", 1) \ No newline at end of file diff --git a/erpnext/patches/v10_0/taxes_issue_with_pos.py b/erpnext/patches/v10_0/taxes_issue_with_pos.py deleted file mode 100644 index 2a3275ac2c..0000000000 --- a/erpnext/patches/v10_0/taxes_issue_with_pos.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - for d in frappe.get_all('Sales Invoice', fields=["name"], - filters = {'is_pos':1, 'docstatus': 1, 'creation': ('>', '2018-04-23')}): - doc = frappe.get_doc('Sales Invoice', d.name) - if (not doc.taxes and doc.taxes_and_charges and doc.pos_profile and doc.outstanding_amount != 0 and - frappe.db.get_value('POS Profile', doc.pos_profile, 'taxes_and_charges', cache=True) == doc.taxes_and_charges): - - doc.append_taxes_from_master() - doc.calculate_taxes_and_totals() - for d in doc.taxes: - d.db_update() - - doc.db_update() - - delete_gle_for_voucher(doc.name) - doc.make_gl_entries() - -def delete_gle_for_voucher(voucher_no): - frappe.db.sql("""delete from `tabGL Entry` where voucher_no = %(voucher_no)s""", - {'voucher_no': voucher_no}) \ No newline at end of file diff --git a/erpnext/patches/v10_0/update_address_template_for_india.py b/erpnext/patches/v10_0/update_address_template_for_india.py deleted file mode 100644 index 1ddca93760..0000000000 --- a/erpnext/patches/v10_0/update_address_template_for_india.py +++ /dev/null @@ -1,12 +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 erpnext.regional.address_template.setup import set_up_address_templates - -def execute(): - if frappe.db.get_value('Company', {'country': 'India'}, 'name'): - address_template = frappe.db.get_value('Address Template', 'India', 'template') - if not address_template or "gstin" not in address_template: - set_up_address_templates(default_country='India') diff --git a/erpnext/patches/v10_0/update_assessment_plan.py b/erpnext/patches/v10_0/update_assessment_plan.py deleted file mode 100644 index 174623c1a4..0000000000 --- a/erpnext/patches/v10_0/update_assessment_plan.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('education', 'doctype', 'assessment_plan') - - frappe.db.sql(""" - UPDATE `tabAssessment Plan` as ap - INNER JOIN `tabStudent Group` as sg ON sg.name = ap.student_group - SET ap.academic_term = sg.academic_term, - ap.academic_year = sg.academic_year, - ap.program = sg.program - WHERE ap.docstatus = 1 - """) \ No newline at end of file diff --git a/erpnext/patches/v10_0/update_assessment_result.py b/erpnext/patches/v10_0/update_assessment_result.py deleted file mode 100644 index 96218db972..0000000000 --- a/erpnext/patches/v10_0/update_assessment_result.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('education', 'doctype', 'assessment_result') - - frappe.db.sql(""" - UPDATE `tabAssessment Result` AS ar - INNER JOIN `tabAssessment Plan` AS ap ON ap.name = ar.assessment_plan - SET ar.academic_term = ap.academic_term, - ar.academic_year = ap.academic_year, - ar.program = ap.program, - ar.course = ap.course, - ar.assessment_group = ap.assessment_group, - ar.student_group = ap.student_group - WHERE ap.docstatus = 1 - """) \ No newline at end of file diff --git a/erpnext/patches/v10_0/update_asset_calculate_depreciation.py b/erpnext/patches/v10_0/update_asset_calculate_depreciation.py deleted file mode 100644 index b947a40b4a..0000000000 --- a/erpnext/patches/v10_0/update_asset_calculate_depreciation.py +++ /dev/null @@ -1,12 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('assets', 'doctype', 'asset') - frappe.reload_doc('assets', 'doctype', 'depreciation_schedule') - - frappe.db.sql(""" - update tabAsset a - set calculate_depreciation = 1 - where exists(select ds.name from `tabDepreciation Schedule` ds where ds.parent=a.name) - """) \ No newline at end of file diff --git a/erpnext/patches/v10_0/update_hub_connector_domain.py b/erpnext/patches/v10_0/update_hub_connector_domain.py deleted file mode 100644 index baf580a369..0000000000 --- a/erpnext/patches/v10_0/update_hub_connector_domain.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.table_exists("Data Migration Connector"): - frappe.db.sql(""" - UPDATE `tabData Migration Connector` - SET hostname = 'https://hubmarket.org' - WHERE connector_name = 'Hub Connector' - """) \ No newline at end of file diff --git a/erpnext/patches/v10_0/update_lft_rgt_for_employee.py b/erpnext/patches/v10_0/update_lft_rgt_for_employee.py deleted file mode 100644 index 46ca786e0d..0000000000 --- a/erpnext/patches/v10_0/update_lft_rgt_for_employee.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.utils.nestedset import rebuild_tree - -def execute(): - """ assign lft and rgt appropriately """ - frappe.reload_doc("hr", "doctype", "employee") - - rebuild_tree("Employee", "reports_to") \ No newline at end of file diff --git a/erpnext/patches/v10_0/update_project_in_sle.py b/erpnext/patches/v10_0/update_project_in_sle.py deleted file mode 100644 index 08c64f18d8..0000000000 --- a/erpnext/patches/v10_0/update_project_in_sle.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - for doctype in ['Sales Invoice', 'Delivery Note', 'Stock Entry']: - frappe.db.sql(""" update - `tabStock Ledger Entry` sle, `tab{0}` parent_doc - set - sle.project = parent_doc.project - where - sle.voucher_no = parent_doc.name and sle.voucher_type = %s and sle.project is null - and parent_doc.project is not null and parent_doc.project != ''""".format(doctype), doctype) diff --git a/erpnext/patches/v10_0/update_reserved_qty_for_purchase_order.py b/erpnext/patches/v10_0/update_reserved_qty_for_purchase_order.py deleted file mode 100644 index 7b2c36698a..0000000000 --- a/erpnext/patches/v10_0/update_reserved_qty_for_purchase_order.py +++ /dev/null @@ -1,53 +0,0 @@ -from __future__ import unicode_literals -import frappe -from erpnext.stock.utils import get_bin - -def execute(): - po_item = list(frappe.db.sql((""" - select distinct po.name as poname, poitem.rm_item_code as rm_item_code, po.company - from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitem - where po.name = poitem.parent - and po.is_subcontracted = "Yes" - and po.docstatus = 1"""), as_dict=1)) - if not po_item: - return - - frappe.reload_doc("stock", "doctype", "bin") - frappe.reload_doc("buying", "doctype", "purchase_order_item_supplied") - company_warehouse = frappe._dict(frappe.db.sql("""select company, min(name) from `tabWarehouse` - where is_group = 0 group by company""")) - - items = list(set([d.rm_item_code for d in po_item])) - item_wh = frappe._dict(frappe.db.sql("""select item_code, default_warehouse - from `tabItem` where name in ({0})""".format(", ".join(["%s"] * len(items))), items)) - - # Update reserved warehouse - for item in po_item: - reserve_warehouse = get_warehouse(item.rm_item_code, item.company, company_warehouse, item_wh) - frappe.db.sql("""update `tabPurchase Order Item Supplied` - set reserve_warehouse = %s - where parent = %s and rm_item_code = %s - """, (reserve_warehouse, item["poname"], item["rm_item_code"])) - - # Update bin - item_wh_bin = frappe.db.sql((""" - select distinct poitemsup.rm_item_code as rm_item_code, - poitemsup.reserve_warehouse as reserve_warehouse - from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup - where po.name = poitemsup.parent - and po.is_subcontracted = "Yes" - and po.docstatus = 1"""), as_dict=1) - for d in item_wh_bin: - try: - stock_bin = get_bin(d["rm_item_code"], d["reserve_warehouse"]) - stock_bin.update_reserved_qty_for_sub_contracting() - except: - pass - -def get_warehouse(item_code, company, company_warehouse, item_wh): - reserve_warehouse = item_wh.get(item_code) - if frappe.db.get_value("Warehouse", reserve_warehouse, "company") != company: - reserve_warehouse = None - if not reserve_warehouse: - reserve_warehouse = company_warehouse.get(company) - return reserve_warehouse diff --git a/erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py b/erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py deleted file mode 100644 index b4f58384bf..0000000000 --- a/erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py +++ /dev/null @@ -1,18 +0,0 @@ -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("buying", "doctype", "supplier_quotation_item") - - for doctype in ['Purchase Order','Supplier Quotation']: - frappe.db.sql(""" - Update - `tab{doctype} Item`, `tabMaterial Request Item` - set - `tab{doctype} Item`.sales_order = `tabMaterial Request Item`.sales_order - where - `tab{doctype} Item`.material_request= `tabMaterial Request Item`.parent - and `tab{doctype} Item`.material_request_item = `tabMaterial Request Item`.name - and `tabMaterial Request Item`.sales_order is not null""".format(doctype=doctype)) \ No newline at end of file diff --git a/erpnext/patches/v10_0/update_status_for_multiple_source_in_po.py b/erpnext/patches/v10_0/update_status_for_multiple_source_in_po.py deleted file mode 100644 index fd3be08b89..0000000000 --- a/erpnext/patches/v10_0/update_status_for_multiple_source_in_po.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - - - # update the sales order item in the material request - frappe.reload_doc('stock', 'doctype', 'material_request_item') - frappe.db.sql('''update `tabMaterial Request Item` mri, `tabSales Order Item` soi - set mri.sales_order_item = soi.name - where ifnull(mri.sales_order, "")!="" and soi.parent=mri.sales_order - and soi.item_code=mri.item_code and mri.docstatus=1 - ''') - - # update the sales order item in the purchase order - frappe.db.sql('''update `tabPurchase Order Item` poi, `tabSales Order Item` soi - set poi.sales_order_item = soi.name - where ifnull(poi.sales_order, "")!="" and soi.parent=poi.sales_order - and soi.item_code=poi.item_code and poi.docstatus = 1 - ''') - - # Update the status in material request and sales order - po_list = frappe.db.sql(''' - select parent from `tabPurchase Order Item` where ifnull(material_request, "")!="" and - ifnull(sales_order, "")!="" and docstatus=1 - ''',as_dict=1) - - for po in list(set([d.get("parent") for d in po_list if d.get("parent")])): - try: - po_doc = frappe.get_doc("Purchase Order", po) - - # update the so in the status updater - po_doc.update_status_updater() - po_doc.update_qty(update_modified=False) - - except Exception: - pass diff --git a/erpnext/patches/v10_0/update_status_in_purchase_receipt.py b/erpnext/patches/v10_0/update_status_in_purchase_receipt.py deleted file mode 100644 index a0bdd9e2cc..0000000000 --- a/erpnext/patches/v10_0/update_status_in_purchase_receipt.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("stock", "doctype", "purchase_receipt") - frappe.db.sql(''' - UPDATE `tabPurchase Receipt` SET status = "Completed" WHERE per_billed = 100 AND docstatus = 1 - ''') \ No newline at end of file diff --git a/erpnext/patches/v10_0/update_territory_and_customer_group.py b/erpnext/patches/v10_0/update_territory_and_customer_group.py deleted file mode 100644 index 7f3dae991d..0000000000 --- a/erpnext/patches/v10_0/update_territory_and_customer_group.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model.rename_doc import get_fetch_fields - -def execute(): - ignore_doctypes = ["Lead", "Opportunity", "POS Profile", "Tax Rule", "Pricing Rule"] - customers = frappe.get_all('Customer', fields=["name", "customer_group"]) - customer_group_fetch = get_fetch_fields('Customer', 'Customer Group', ignore_doctypes) - - batch_size = 1000 - for i in range(0, len(customers), batch_size): - batch_customers = customers[i:i + batch_size] - for d in customer_group_fetch: - when_then = [] - for customer in batch_customers: - value = frappe.db.escape(frappe.as_unicode(customer.get("customer_group"))) - - when_then.append(''' - WHEN `%s` = %s and %s != %s - THEN %s - '''%(d["master_fieldname"], frappe.db.escape(frappe.as_unicode(customer.name)), - d["linked_to_fieldname"], value, value)) - - frappe.db.sql(""" - update - `tab%s` - set - %s = CASE %s ELSE `%s` END - """%(d['doctype'], d.linked_to_fieldname, " ".join(when_then), d.linked_to_fieldname)) diff --git a/erpnext/patches/v10_0/update_user_image_in_employee.py b/erpnext/patches/v10_0/update_user_image_in_employee.py deleted file mode 100644 index 72d5d2a857..0000000000 --- a/erpnext/patches/v10_0/update_user_image_in_employee.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('hr', 'doctype', 'employee') - - frappe.db.sql(""" - UPDATE - `tabEmployee`, `tabUser` - SET - `tabEmployee`.image = `tabUser`.user_image - WHERE - `tabEmployee`.user_id = `tabUser`.name and - `tabEmployee`.user_id is not null and - `tabEmployee`.user_id != '' and `tabEmployee`.image is null - """) diff --git a/erpnext/patches/v10_0/update_warehouse_address_details.py b/erpnext/patches/v10_0/update_warehouse_address_details.py deleted file mode 100644 index b982b9a662..0000000000 --- a/erpnext/patches/v10_0/update_warehouse_address_details.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - warehouse = frappe.db.sql("""select name, email_id, phone_no, mobile_no, address_line_1, - address_line_2, city, state, pin from `tabWarehouse` where ifnull(address_line_1, '') != '' - or ifnull(mobile_no, '') != '' - or ifnull(email_id, '') != '' """, as_dict=1) - - for d in warehouse: - try: - address = frappe.new_doc('Address') - address.name = d.name - address.address_title = d.name - address.address_line1 = d.address_line_1 - address.city = d.city - address.state = d.state - address.pincode = d.pin - address.db_insert() - address.append('links',{'link_doctype':'Warehouse','link_name':d.name}) - address.links[0].db_insert() - if d.name and (d.email_id or d.mobile_no or d.phone_no): - contact = frappe.new_doc('Contact') - contact.name = d.name - contact.first_name = d.name - contact.mobile_no = d.mobile_no - contact.email_id = d.email_id - contact.phone = d.phone_no - contact.db_insert() - contact.append('links',{'link_doctype':'Warehouse','link_name':d.name}) - contact.links[0].db_insert() - except frappe.DuplicateEntryError: - pass - \ No newline at end of file diff --git a/erpnext/patches/v10_1/__init__.py b/erpnext/patches/v10_1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v11_0/__init__.py b/erpnext/patches/v11_0/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v11_0/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v11_0/remove_subscriber_doctype.py b/erpnext/patches/v11_0/remove_subscriber_doctype.py deleted file mode 100644 index 4839a20f91..0000000000 --- a/erpnext/patches/v11_0/remove_subscriber_doctype.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import rename_field - -def execute(): - """ copy subscribe field to customer """ - frappe.reload_doc("accounts","doctype","subscription") - - if frappe.db.exists("DocType", "Subscriber"): - if frappe.db.has_column('Subscription','subscriber'): - frappe.db.sql(""" - update `tabSubscription` s1 - set customer=(select customer from tabSubscriber where name=s1.subscriber) - """) - - frappe.delete_doc("DocType", "Subscriber") \ No newline at end of file diff --git a/erpnext/patches/v11_1/__init__.py b/erpnext/patches/v11_1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v12_0/__init__.py b/erpnext/patches/v12_0/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v13_0/__init__.py b/erpnext/patches/v13_0/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v13_0/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v4_0/__init__.py b/erpnext/patches/v4_0/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v4_0/apply_user_permissions.py b/erpnext/patches/v4_0/apply_user_permissions.py deleted file mode 100644 index 3c5d612c18..0000000000 --- a/erpnext/patches/v4_0/apply_user_permissions.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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 erpnext.hr.doctype.employee.employee import EmployeeUserDisabledError - -def execute(): - update_hr_permissions() - update_permissions() - remove_duplicate_user_permissions() - frappe.clear_cache() - -def update_hr_permissions(): - # add set user permissions rights to HR Manager - frappe.db.sql("""update `tabDocPerm` set `set_user_permissions`=1 where parent in ('Employee', 'Leave Application') - and role='HR Manager' and permlevel=0 and `read`=1""") - docperm_meta = frappe.get_meta('DocPerm') - if docperm_meta.get_field('apply_user_permissions'): - # apply user permissions on Employee and Leave Application - frappe.db.sql("""update `tabDocPerm` set `apply_user_permissions`=1 where parent in ('Employee', 'Leave Application') - and role in ('Employee', 'Leave Approver') and permlevel=0 and `read`=1""") - - frappe.clear_cache() - - # save employees to run on_update events - for employee in frappe.db.sql_list("""select name from `tabEmployee` where docstatus < 2"""): - try: - emp = frappe.get_doc("Employee", employee) - emp.flags.ignore_mandatory = True - emp.save() - except EmployeeUserDisabledError: - pass - -def update_permissions(): - # clear match conditions other than owner - frappe.db.sql("""update tabDocPerm set `match`='' - where ifnull(`match`,'') not in ('', 'owner')""") - -def remove_duplicate_user_permissions(): - # remove duplicate user_permissions (if they exist) - for d in frappe.db.sql("""select parent, defkey, defvalue, - count(*) as cnt from tabDefaultValue - where parent not in ('__global', '__default') - group by parent, defkey, defvalue""", as_dict=1): - if d.cnt > 1: - # order by parenttype so that user permission does not get removed! - frappe.db.sql("""delete from tabDefaultValue where `parent`=%s and `defkey`=%s and - `defvalue`=%s order by parenttype limit %s""", (d.parent, d.defkey, d.defvalue, d.cnt-1)) - diff --git a/erpnext/patches/v4_0/countrywise_coa.py b/erpnext/patches/v4_0/countrywise_coa.py deleted file mode 100644 index f45e6028c8..0000000000 --- a/erpnext/patches/v4_0/countrywise_coa.py +++ /dev/null @@ -1,29 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doc("setup", 'doctype', "company") - frappe.reload_doc("accounts", 'doctype', "account") - - frappe.db.sql("""update tabAccount set account_type='Cash' - where account_type='Bank or Cash' and account_name in ('Cash', 'Cash In Hand')""") - - frappe.db.sql("""update tabAccount set account_type='Stock' - where account_name = 'Stock Assets'""") - - ac_types = {"Fixed Asset Account": "Fixed Asset", "Bank or Cash": "Bank"} - for old, new in ac_types.items(): - frappe.db.sql("""update tabAccount set account_type=%s - where account_type=%s""", (new, old)) - - try: - frappe.db.sql("""update `tabAccount` set report_type = - if(is_pl_account='Yes', 'Profit and Loss', 'Balance Sheet')""") - - frappe.db.sql("""update `tabAccount` set balance_must_be=debit_or_credit - where ifnull(allow_negative_balance, 0) = 0""") - except: - pass diff --git a/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py b/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py deleted file mode 100644 index fe50e444b5..0000000000 --- a/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py +++ /dev/null @@ -1,63 +0,0 @@ -# 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.custom.doctype.custom_field.custom_field import create_custom_field_if_values_exist - -def execute(): - frappe.reload_doc("stock", "doctype", "purchase_receipt") - frappe.reload_doc("hr", "doctype", "employee") - frappe.reload_doc("Payroll", "doctype", "salary_slip") - - india_specific_fields = { - "Purchase Receipt": [{ - "label": "Supplier Shipment No", - "fieldname": "challan_no", - "fieldtype": "Data", - "insert_after": "is_subcontracted" - }, { - "label": "Supplier Shipment Date", - "fieldname": "challan_date", - "fieldtype": "Date", - "insert_after": "is_subcontracted" - }], - "Employee": [{ - "label": "PAN Number", - "fieldname": "pan_number", - "fieldtype": "Data", - "insert_after": "company_email" - }, { - "label": "Gratuity LIC Id", - "fieldname": "gratuity_lic_id", - "fieldtype": "Data", - "insert_after": "company_email" - }, { - "label": "Esic Card No", - "fieldname": "esic_card_no", - "fieldtype": "Data", - "insert_after": "bank_ac_no" - }, { - "label": "PF Number", - "fieldname": "pf_number", - "fieldtype": "Data", - "insert_after": "bank_ac_no" - }], - "Salary Slip": [{ - "label": "Esic No", - "fieldname": "esic_no", - "fieldtype": "Data", - "insert_after": "letter_head", - "permlevel": 1 - }, { - "label": "PF Number", - "fieldname": "pf_no", - "fieldtype": "Data", - "insert_after": "letter_head", - "permlevel": 1 - }] - } - - for dt, docfields in india_specific_fields.items(): - for df in docfields: - create_custom_field_if_values_exist(dt, df) diff --git a/erpnext/patches/v4_0/create_price_list_if_missing.py b/erpnext/patches/v4_0/create_price_list_if_missing.py deleted file mode 100644 index 039e52111c..0000000000 --- a/erpnext/patches/v4_0/create_price_list_if_missing.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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 import _ -from frappe.utils.nestedset import get_root_of - -def execute(): - # setup not complete - if not frappe.db.sql("""select name from tabCompany limit 1"""): - return - - if "shopping_cart" in frappe.get_installed_apps(): - frappe.reload_doc("shopping_cart", "doctype", "shopping_cart_settings") - - if not frappe.db.sql("select name from `tabPrice List` where buying=1"): - create_price_list(_("Standard Buying"), buying=1) - - if not frappe.db.sql("select name from `tabPrice List` where selling=1"): - create_price_list(_("Standard Selling"), selling=1) - -def create_price_list(pl_name, buying=0, selling=0): - price_list = frappe.get_doc({ - "doctype": "Price List", - "price_list_name": pl_name, - "enabled": 1, - "buying": buying, - "selling": selling, - "currency": frappe.db.get_default("currency"), - "territories": [{ - "territory": get_root_of("Territory") - }] - }) - price_list.insert() diff --git a/erpnext/patches/v4_0/customer_discount_to_pricing_rule.py b/erpnext/patches/v4_0/customer_discount_to_pricing_rule.py deleted file mode 100644 index 1b260c48cc..0000000000 --- a/erpnext/patches/v4_0/customer_discount_to_pricing_rule.py +++ /dev/null @@ -1,33 +0,0 @@ -# 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.nestedset import get_root_of - -def execute(): - frappe.reload_doc("accounts", "doctype", "pricing_rule") - - frappe.db.auto_commit_on_many_writes = True - - default_item_group = get_root_of("Item Group") - - for d in frappe.db.sql("""select * from `tabCustomer Discount` - where ifnull(parent, '') != ''""", as_dict=1): - if not d.discount: - continue - - frappe.get_doc({ - "doctype": "Pricing Rule", - "apply_on": "Item Group", - "item_group": d.item_group or default_item_group, - "applicable_for": "Customer", - "customer": d.parent, - "price_or_discount": "Discount Percentage", - "discount_percentage": d.discount, - "selling": 1 - }).insert() - - frappe.db.auto_commit_on_many_writes = False - - frappe.delete_doc("DocType", "Customer Discount") diff --git a/erpnext/patches/v4_0/fields_to_be_renamed.py b/erpnext/patches/v4_0/fields_to_be_renamed.py deleted file mode 100644 index cc17697132..0000000000 --- a/erpnext/patches/v4_0/fields_to_be_renamed.py +++ /dev/null @@ -1,109 +0,0 @@ -# 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.model.utils.rename_field import rename_field -from frappe.modules import scrub, get_doctype_module - -rename_map = { - "Quotation Item": [ - ["ref_rate", "price_list_rate"], - ["base_ref_rate", "base_price_list_rate"], - ["adj_rate", "discount_percentage"], - ["export_rate", "rate"], - ["basic_rate", "base_rate"], - ["amount", "base_amount"], - ["export_amount", "amount"] - ], - - "Sales Order Item": [ - ["ref_rate", "price_list_rate"], - ["base_ref_rate", "base_price_list_rate"], - ["adj_rate", "discount_percentage"], - ["export_rate", "rate"], - ["basic_rate", "base_rate"], - ["amount", "base_amount"], - ["export_amount", "amount"], - ["reserved_warehouse", "warehouse"] - ], - - "Delivery Note Item": [ - ["ref_rate", "price_list_rate"], - ["base_ref_rate", "base_price_list_rate"], - ["adj_rate", "discount_percentage"], - ["export_rate", "rate"], - ["basic_rate", "base_rate"], - ["amount", "base_amount"], - ["export_amount", "amount"] - ], - - "Sales Invoice Item": [ - ["ref_rate", "price_list_rate"], - ["base_ref_rate", "base_price_list_rate"], - ["adj_rate", "discount_percentage"], - ["export_rate", "rate"], - ["basic_rate", "base_rate"], - ["amount", "base_amount"], - ["export_amount", "amount"] - ], - - "Supplier Quotation Item": [ - ["import_ref_rate", "price_list_rate"], - ["purchase_ref_rate", "base_price_list_rate"], - ["discount_rate", "discount_percentage"], - ["import_rate", "rate"], - ["purchase_rate", "base_rate"], - ["amount", "base_amount"], - ["import_amount", "amount"] - ], - - "Purchase Order Item": [ - ["import_ref_rate", "price_list_rate"], - ["purchase_ref_rate", "base_price_list_rate"], - ["discount_rate", "discount_percentage"], - ["import_rate", "rate"], - ["purchase_rate", "base_rate"], - ["amount", "base_amount"], - ["import_amount", "amount"] - ], - - "Purchase Receipt Item": [ - ["import_ref_rate", "price_list_rate"], - ["purchase_ref_rate", "base_price_list_rate"], - ["discount_rate", "discount_percentage"], - ["import_rate", "rate"], - ["purchase_rate", "base_rate"], - ["amount", "base_amount"], - ["import_amount", "amount"] - ], - - "Purchase Invoice Item": [ - ["import_ref_rate", "price_list_rate"], - ["purchase_ref_rate", "base_price_list_rate"], - ["discount_rate", "discount_percentage"], - ["import_rate", "rate"], - ["rate", "base_rate"], - ["amount", "base_amount"], - ["import_amount", "amount"], - ["expense_head", "expense_account"] - ], - - "Item": [ - ["purchase_account", "expense_account"], - ["default_sales_cost_center", "selling_cost_center"], - ["cost_center", "buying_cost_center"], - ["default_income_account", "income_account"], - ], - "Item Price": [ - ["ref_rate", "price_list_rate"] - ] -} - -def execute(): - for dn in rename_map: - frappe.reload_doc(get_doctype_module(dn), "doctype", scrub(dn)) - - for dt, field_list in rename_map.items(): - for field in field_list: - rename_field(dt, field[0], field[1]) diff --git a/erpnext/patches/v4_0/fix_address_template.py b/erpnext/patches/v4_0/fix_address_template.py deleted file mode 100644 index 905e3db3e8..0000000000 --- a/erpnext/patches/v4_0/fix_address_template.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors - -from __future__ import unicode_literals -import frappe - -def execute(): - missing_line = """{{ address_line1 }}
""" - for name, template in frappe.db.sql("select name, template from `tabAddress Template`"): - if missing_line not in template: - d = frappe.get_doc("Address Template", name) - d.template = missing_line + d.template - d.save() diff --git a/erpnext/patches/v4_0/fix_case_of_hr_module_def.py b/erpnext/patches/v4_0/fix_case_of_hr_module_def.py deleted file mode 100644 index e77b427b77..0000000000 --- a/erpnext/patches/v4_0/fix_case_of_hr_module_def.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors - -from __future__ import unicode_literals -import frappe - -def execute(): - hr = frappe.db.get_value("Module Def", "HR") - if hr == "Hr": - frappe.rename_doc("Module Def", "Hr", "HR") - frappe.db.set_value("Module Def", "HR", "module_name", "HR") - - frappe.clear_cache() - frappe.setup_module_map() diff --git a/erpnext/patches/v4_0/fix_contact_address.py b/erpnext/patches/v4_0/fix_contact_address.py deleted file mode 100644 index 6a3e106b8c..0000000000 --- a/erpnext/patches/v4_0/fix_contact_address.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("website", "doctype", "contact_us_settings") - address = frappe.db.get_value("Contact Us Settings", None, "address") - if address: - address = frappe.get_doc("Address", address) - contact = frappe.get_doc("Contact Us Settings", "Contact Us Settings") - for f in ("address_title", "address_line1", "address_line2", "city", "state", "country", "pincode"): - contact.set(f, address.get(f)) - - contact.save() \ No newline at end of file diff --git a/erpnext/patches/v4_0/fix_employee_user_id.py b/erpnext/patches/v4_0/fix_employee_user_id.py deleted file mode 100644 index 6f449f97bb..0000000000 --- a/erpnext/patches/v4_0/fix_employee_user_id.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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 import get_fullname - -def execute(): - for user_id in frappe.db.sql_list("""select distinct user_id from `tabEmployee` - where ifnull(user_id, '')!='' - group by user_id having count(name) > 1"""): - - fullname = get_fullname(user_id) - employee = frappe.db.get_value("Employee", {"employee_name": fullname, "user_id": user_id}) - - if employee: - frappe.db.sql("""update `tabEmployee` set user_id=null - where user_id=%s and name!=%s""", (user_id, employee)) - else: - count = frappe.db.sql("""select count(*) from `tabEmployee` where user_id=%s""", user_id)[0][0] - frappe.db.sql("""update `tabEmployee` set user_id=null - where user_id=%s limit %s""", (user_id, count - 1)) diff --git a/erpnext/patches/v4_0/global_defaults_to_system_settings.py b/erpnext/patches/v4_0/global_defaults_to_system_settings.py deleted file mode 100644 index 2905fe1e6a..0000000000 --- a/erpnext/patches/v4_0/global_defaults_to_system_settings.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals - -import frappe -from collections import Counter -from frappe.core.doctype.user.user import STANDARD_USERS - -def execute(): - frappe.reload_doc("core", "doctype", "system_settings") - system_settings = frappe.get_doc("System Settings") - - # set values from global_defauls - global_defaults = frappe.db.get_value("Global Defaults", None, - ["time_zone", "date_format", "number_format", "float_precision", "session_expiry"], as_dict=True) - - if global_defaults: - for key, val in global_defaults.items(): - if not system_settings.get(key): - system_settings.set(key, val) - - # language - if not system_settings.get("language"): - # find most common language - lang = frappe.db.sql_list("""select language from `tabUser` - where ifnull(language, '')!='' and language not like "Loading%%" and name not in ({standard_users})""".format( - standard_users=", ".join(["%s"]*len(STANDARD_USERS))), tuple(STANDARD_USERS)) - lang = Counter(lang).most_common(1) - lang = (len(lang) > 0) and lang[0][0] or "english" - - system_settings.language = lang - - system_settings.flags.ignore_mandatory = True - system_settings.save() - - global_defaults = frappe.get_doc("Global Defaults") - global_defaults.flags.ignore_mandatory = True - global_defaults.save() diff --git a/erpnext/patches/v4_0/import_country_codes.py b/erpnext/patches/v4_0/import_country_codes.py deleted file mode 100644 index 43e23d5b63..0000000000 --- a/erpnext/patches/v4_0/import_country_codes.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals -import frappe -from frappe.geo.country_info import get_all -from frappe.utils.install import import_country_and_currency - -from six import iteritems - -def execute(): - frappe.reload_doc("setup", "doctype", "country") - import_country_and_currency() - for name, country in iteritems(get_all()): - frappe.set_value("Country", name, "code", country.get("code")) \ No newline at end of file diff --git a/erpnext/patches/v4_0/map_charge_to_taxes_and_charges.py b/erpnext/patches/v4_0/map_charge_to_taxes_and_charges.py deleted file mode 100644 index 97e217aa05..0000000000 --- a/erpnext/patches/v4_0/map_charge_to_taxes_and_charges.py +++ /dev/null @@ -1,16 +0,0 @@ -# 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 - -def execute(): - # update sales cycle - for d in ['Sales Invoice', 'Sales Order', 'Quotation', 'Delivery Note']: - frappe.db.sql("""update `tab%s` set taxes_and_charges=charge""" % d) - - # update purchase cycle - for d in ['Purchase Invoice', 'Purchase Order', 'Supplier Quotation', 'Purchase Receipt']: - frappe.db.sql("""update `tab%s` set taxes_and_charges=purchase_other_charges""" % d) - - frappe.db.sql("""update `tabPurchase Taxes and Charges` set parentfield='other_charges'""") diff --git a/erpnext/patches/v4_0/move_warehouse_user_to_restrictions.py b/erpnext/patches/v4_0/move_warehouse_user_to_restrictions.py deleted file mode 100644 index 8b81936d8d..0000000000 --- a/erpnext/patches/v4_0/move_warehouse_user_to_restrictions.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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 -import frappe.permissions - -def execute(): - for warehouse, user in frappe.db.sql("""select parent, user from `tabWarehouse User`"""): - frappe.permissions.add_user_permission("Warehouse", warehouse, user) - - frappe.delete_doc_if_exists("DocType", "Warehouse User") - frappe.reload_doc("stock", "doctype", "warehouse") diff --git a/erpnext/patches/v4_0/new_address_template.py b/erpnext/patches/v4_0/new_address_template.py deleted file mode 100644 index fa6602706e..0000000000 --- a/erpnext/patches/v4_0/new_address_template.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import print_function, unicode_literals -import frappe - -def execute(): - frappe.reload_doc("utilities", "doctype", "address_template") - if not frappe.db.sql("select name from `tabAddress Template`"): - try: - d = frappe.new_doc("Address Template") - d.update({"country":frappe.db.get_default("country") or - frappe.db.get_value("Global Defaults", "Global Defaults", "country")}) - d.insert() - except: - print(frappe.get_traceback()) - diff --git a/erpnext/patches/v4_0/reload_sales_print_format.py b/erpnext/patches/v4_0/reload_sales_print_format.py deleted file mode 100644 index b8f090f9f3..0000000000 --- a/erpnext/patches/v4_0/reload_sales_print_format.py +++ /dev/null @@ -1,8 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doc('accounts', 'Print Format', 'POS Invoice') diff --git a/erpnext/patches/v4_0/remove_employee_role_if_no_employee.py b/erpnext/patches/v4_0/remove_employee_role_if_no_employee.py deleted file mode 100644 index 8766ace54f..0000000000 --- a/erpnext/patches/v4_0/remove_employee_role_if_no_employee.py +++ /dev/null @@ -1,18 +0,0 @@ -# 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 -import frappe.permissions - -def execute(): - for user in frappe.db.sql_list("select distinct parent from `tabHas Role` where role='Employee'"): - # if employee record does not exists, remove employee role! - if not frappe.db.get_value("Employee", {"user_id": user}): - try: - user = frappe.get_doc("User", user) - for role in user.get("roles", {"role": "Employee"}): - user.get("roles").remove(role) - user.save() - except frappe.DoesNotExistError: - pass diff --git a/erpnext/patches/v4_0/remove_module_home_pages.py b/erpnext/patches/v4_0/remove_module_home_pages.py deleted file mode 100644 index a2720e0ebf..0000000000 --- a/erpnext/patches/v4_0/remove_module_home_pages.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - -def execute(): - for page in ("accounts-home", "website-home", "support-home", "stock-home", "selling-home", "projects-home", - "manufacturing-home", "hr-home", "buying-home"): - frappe.delete_doc("Page", page) \ No newline at end of file diff --git a/erpnext/patches/v4_0/rename_sitemap_to_route.py b/erpnext/patches/v4_0/rename_sitemap_to_route.py deleted file mode 100644 index ffb1fda144..0000000000 --- a/erpnext/patches/v4_0/rename_sitemap_to_route.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import unicode_literals -import frappe -import frappe.model - -def execute(): - frappe.reload_doc("setup", "doctype", "item_group") - frappe.reload_doc("stock", "doctype", "item") - frappe.reload_doc("setup", "doctype", "sales_partner") - - try: - frappe.model.rename_field("Item Group", "parent_website_sitemap", "parent_website_route") - frappe.model.rename_field("Item", "parent_website_sitemap", "parent_website_route") - frappe.model.rename_field("Sales Partner", "parent_website_sitemap", - "parent_website_route") - except Exception as e: - if e.args[0]!=1054: - raise diff --git a/erpnext/patches/v4_0/reset_permissions_for_masters.py b/erpnext/patches/v4_0/reset_permissions_for_masters.py deleted file mode 100644 index bc1b438e2b..0000000000 --- a/erpnext/patches/v4_0/reset_permissions_for_masters.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -from frappe.permissions import reset_perms - -def execute(): - for doctype in ("About Us Settings", "Accounts Settings", "Activity Type", - "Blog Category", "Blog Settings", "Blogger", "Branch", "Brand", "Buying Settings", - "Communication", "Company", "Contact Us Settings", - "Country", "Currency", "Currency Exchange", "Deduction Type", "Department", - "Designation", "Earning Type", "Event", "Feed", "File", "Fiscal Year", - "HR Settings", "Industry Type", "Leave Type", "Letter Head", - "Mode of Payment", "Module Def", "Naming Series", "POS Setting", "Print Heading", - "Report", "Role", "Selling Settings", "Stock Settings", "Supplier Type", "UOM"): - try: - reset_perms(doctype) - except: - print("Error resetting perms for", doctype) - raise diff --git a/erpnext/patches/v4_0/save_default_letterhead.py b/erpnext/patches/v4_0/save_default_letterhead.py deleted file mode 100644 index 5d75f096b3..0000000000 --- a/erpnext/patches/v4_0/save_default_letterhead.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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 - -def execute(): - """save default letterhead to set default_letter_head_content""" - try: - letter_head = frappe.get_doc("Letter Head", {"is_default": 1}) - letter_head.save() - except frappe.DoesNotExistError: - pass diff --git a/erpnext/patches/v4_0/set_pricing_rule_for_buying_or_selling.py b/erpnext/patches/v4_0/set_pricing_rule_for_buying_or_selling.py deleted file mode 100644 index 7e472e2195..0000000000 --- a/erpnext/patches/v4_0/set_pricing_rule_for_buying_or_selling.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doc("accounts", "doctype", "pricing_rule") - frappe.db.sql("""update `tabPricing Rule` set selling=1 where ifnull(applicable_for, '') in - ('', 'Customer', 'Customer Group', 'Territory', 'Sales Partner', 'Campaign')""") - - frappe.db.sql("""update `tabPricing Rule` set buying=1 where ifnull(applicable_for, '') in - ('', 'Supplier', 'Supplier Type')""") diff --git a/erpnext/patches/v4_0/split_email_settings.py b/erpnext/patches/v4_0/split_email_settings.py deleted file mode 100644 index 5d1dea60ee..0000000000 --- a/erpnext/patches/v4_0/split_email_settings.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe - -def execute(): - print("WARNING!!!! Email Settings not migrated. Please setup your email again.") - - # this will happen if you are migrating very old accounts - # comment out this line below and remember to create new Email Accounts - # for incoming and outgoing mails - raise Exception - - return - - - frappe.reload_doc("core", "doctype", "outgoing_email_settings") - frappe.reload_doc("support", "doctype", "support_email_settings") - - email_settings = get_email_settings() - map_outgoing_email_settings(email_settings) - map_support_email_settings(email_settings) - - -def map_outgoing_email_settings(email_settings): - outgoing_email_settings = frappe.get_doc("Outgoing Email Settings") - for fieldname in (("outgoing_mail_server", "mail_server"), - "use_ssl", "mail_port", "mail_login", "mail_password", - "always_use_login_id_as_sender", "auto_email_id"): - - if isinstance(fieldname, tuple): - from_fieldname, to_fieldname = fieldname - else: - from_fieldname = to_fieldname = fieldname - - outgoing_email_settings.set(to_fieldname, email_settings.get(from_fieldname)) - - outgoing_email_settings._fix_numeric_types() - outgoing_email_settings.save() - -def map_support_email_settings(email_settings): - support_email_settings = frappe.get_doc("Support Email Settings") - - for fieldname in ("sync_support_mails", "support_email", - ("support_host", "mail_server"), - ("support_use_ssl", "use_ssl"), - ("support_username", "mail_login"), - ("support_password", "mail_password"), - "support_signature", "send_autoreply", "support_autoreply"): - - if isinstance(fieldname, tuple): - from_fieldname, to_fieldname = fieldname - else: - from_fieldname = to_fieldname = fieldname - - support_email_settings.set(to_fieldname, email_settings.get(from_fieldname)) - - support_email_settings._fix_numeric_types() - support_email_settings.save() - -def get_email_settings(): - ret = {} - for field, value in frappe.db.sql("select field, value from tabSingles where doctype='Email Settings'"): - ret[field] = value - return ret - diff --git a/erpnext/patches/v4_0/update_account_root_type.py b/erpnext/patches/v4_0/update_account_root_type.py deleted file mode 100644 index 15ddf032a4..0000000000 --- a/erpnext/patches/v4_0/update_account_root_type.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe - -def execute(): - frappe.reload_doc("accounts", "doctype", "account") - - account_table_columns = frappe.db.get_table_columns("Account") - if "debit_or_credit" in account_table_columns and "is_pl_account" in account_table_columns: - frappe.db.sql("""UPDATE tabAccount - SET root_type = CASE - WHEN (debit_or_credit='Debit' and is_pl_account = 'No') THEN 'Asset' - WHEN (debit_or_credit='Credit' and is_pl_account = 'No') THEN 'Liability' - WHEN (debit_or_credit='Debit' and is_pl_account = 'Yes') THEN 'Expense' - WHEN (debit_or_credit='Credit' and is_pl_account = 'Yes') THEN 'Income' - END - WHERE ifnull(parent_account, '') = '' - """) - - else: - for key, root_type in (("asset", "Asset"), ("liabilities", "Liability"), ("expense", "Expense"), - ("income", "Income")): - frappe.db.sql("""update tabAccount set root_type=%s where name like %s - and ifnull(parent_account, '')=''""", (root_type, "%" + key + "%")) - - for root in frappe.db.sql("""SELECT name, lft, rgt, root_type FROM `tabAccount` - WHERE ifnull(parent_account, '')=''""", as_dict=True): - if root.root_type: - frappe.db.sql("""UPDATE tabAccount SET root_type=%s WHERE lft>%s and rgt<%s""", - (root.root_type, root.lft, root.rgt)) - else: - print(b"Root type not found for {0}".format(root.name.encode("utf-8"))) diff --git a/erpnext/patches/v4_0/update_custom_print_formats_for_renamed_fields.py b/erpnext/patches/v4_0/update_custom_print_formats_for_renamed_fields.py deleted file mode 100644 index d784a1b182..0000000000 --- a/erpnext/patches/v4_0/update_custom_print_formats_for_renamed_fields.py +++ /dev/null @@ -1,36 +0,0 @@ -# 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 -import re - -def execute(): - # NOTE: sequence is important - fields_list = ( - ("amount", "base_amount"), - ("ref_rate", "price_list_rate"), - ("base_ref_rate", "base_price_list_rate"), - ("adj_rate", "discount_percentage"), - ("export_rate", "rate"), - ("basic_rate", "base_rate"), - ("export_amount", "amount"), - ("reserved_warehouse", "warehouse"), - ("import_ref_rate", "price_list_rate"), - ("purchase_ref_rate", "base_price_list_rate"), - ("discount_rate", "discount_percentage"), - ("import_rate", "rate"), - ("purchase_rate", "base_rate"), - ("import_amount", "amount") - ) - - condition = " or ".join("""html like "%%{}%%" """.format(d[0].replace("_", "\\_")) for d in fields_list - if d[0] != "amount") - - for name, html in frappe.db.sql("""select name, html from `tabPrint Format` - where standard = 'No' and ({}) and html not like '%%frappe.%%'""".format(condition)): - html = html.replace("wn.", "frappe.") - for from_field, to_field in fields_list: - html = re.sub(r"\b{}\b".format(from_field), to_field, html) - - frappe.db.set_value("Print Format", name, "html", html) diff --git a/erpnext/patches/v4_0/update_incharge_name_to_sales_person_in_maintenance_schedule.py b/erpnext/patches/v4_0/update_incharge_name_to_sales_person_in_maintenance_schedule.py deleted file mode 100644 index fe66a5e3ef..0000000000 --- a/erpnext/patches/v4_0/update_incharge_name_to_sales_person_in_maintenance_schedule.py +++ /dev/null @@ -1,12 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doc("support", "doctype", "schedules") - frappe.reload_doc("support", "doctype", "maintenance_schedule_item") - - frappe.db.sql("""update `tabMaintenance Schedule Detail` set sales_person=incharge_name""") - frappe.db.sql("""update `tabMaintenance Schedule Item` set sales_person=incharge_name""") \ No newline at end of file diff --git a/erpnext/patches/v4_0/update_other_charges_in_custom_purchase_print_formats.py b/erpnext/patches/v4_0/update_other_charges_in_custom_purchase_print_formats.py deleted file mode 100644 index 2e2e77a9fc..0000000000 --- a/erpnext/patches/v4_0/update_other_charges_in_custom_purchase_print_formats.py +++ /dev/null @@ -1,12 +0,0 @@ -# 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 -import re - -def execute(): - for name, html in frappe.db.sql("""select name, html from `tabPrint Format` - where standard = 'No' and html like '%%purchase\\_tax\\_details%%'"""): - html = re.sub(r"\bpurchase_tax_details\b", "taxes", html) - frappe.db.set_value("Print Format", name, "html", html) diff --git a/erpnext/patches/v4_0/update_tax_amount_after_discount.py b/erpnext/patches/v4_0/update_tax_amount_after_discount.py deleted file mode 100644 index d10329ef37..0000000000 --- a/erpnext/patches/v4_0/update_tax_amount_after_discount.py +++ /dev/null @@ -1,20 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doc("accounts", "doctype", "sales_taxes_and_charges") - docs_with_discount_amount = frappe._dict() - for dt in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]: - records = frappe.db.sql_list("""select name from `tab%s` - where ifnull(discount_amount, 0) > 0 and docstatus=1""" % dt) - docs_with_discount_amount[dt] = records - - for dt, discounted_records in docs_with_discount_amount.items(): - frappe.db.sql("""update `tabSales Taxes and Charges` - set tax_amount_after_discount_amount = tax_amount - where parenttype = %s and parent not in (%s)""" % - ('%s', ', '.join(['%s']*(len(discounted_records)+1))), - tuple([dt, ''] + discounted_records)) diff --git a/erpnext/patches/v4_0/update_user_properties.py b/erpnext/patches/v4_0/update_user_properties.py deleted file mode 100644 index f2085ce4df..0000000000 --- a/erpnext/patches/v4_0/update_user_properties.py +++ /dev/null @@ -1,51 +0,0 @@ -# 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 -import frappe.permissions -import frappe.defaults - -def execute(): - frappe.reload_doc("core", "doctype", "docfield") - frappe.reload_doc("hr", "doctype", "employee") - - set_print_email_permissions() - migrate_user_properties_to_user_permissions() - - frappe.clear_cache() - -def migrate_user_properties_to_user_permissions(): - for d in frappe.db.sql("""select parent, defkey, defvalue from tabDefaultValue - where parent not in ('__global', '__default')""", as_dict=True): - df = frappe.db.sql("""select options from tabDocField - where fieldname=%s and fieldtype='Link'""", d.defkey, as_dict=True) - - if df: - frappe.db.sql("""update tabDefaultValue - set defkey=%s, parenttype='User Permission' - where defkey=%s and - parent not in ('__global', '__default')""", (df[0].options, d.defkey)) - -def set_print_email_permissions(): - # reset Page perms - from frappe.core.page.permission_manager.permission_manager import reset - reset("Page") - reset("Report") - - if "allow_print" not in frappe.db.get_table_columns("DocType"): - return - - # patch to move print, email into DocPerm - # NOTE: allow_print and allow_email are misnamed. They were used to hide print / hide email - for doctype, hide_print, hide_email in frappe.db.sql("""select name, ifnull(allow_print, 0), ifnull(allow_email, 0) - from `tabDocType` where ifnull(issingle, 0)=0 and ifnull(istable, 0)=0 and - (ifnull(allow_print, 0)=0 or ifnull(allow_email, 0)=0)"""): - - if not hide_print: - frappe.db.sql("""update `tabDocPerm` set `print`=1 - where permlevel=0 and `read`=1 and parent=%s""", doctype) - - if not hide_email: - frappe.db.sql("""update `tabDocPerm` set `email`=1 - where permlevel=0 and `read`=1 and parent=%s""", doctype) diff --git a/erpnext/patches/v4_0/update_users_report_view_settings.py b/erpnext/patches/v4_0/update_users_report_view_settings.py deleted file mode 100644 index 6f216f5b38..0000000000 --- a/erpnext/patches/v4_0/update_users_report_view_settings.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals - -from frappe.model.utils.rename_field import update_users_report_view_settings -from erpnext.patches.v4_0.fields_to_be_renamed import rename_map - -def execute(): - for dt, field_list in rename_map.items(): - for field in field_list: - update_users_report_view_settings(dt, field[0], field[1]) diff --git a/erpnext/patches/v4_0/validate_v3_patch.py b/erpnext/patches/v4_0/validate_v3_patch.py deleted file mode 100644 index 3df39edea6..0000000000 --- a/erpnext/patches/v4_0/validate_v3_patch.py +++ /dev/null @@ -1,11 +0,0 @@ -# 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 - -def execute(): - from frappe.modules.patch_handler import executed - last_v3_patch = 'patches.1401.fix_pos_outstanding' - if not executed(last_v3_patch): - raise Exception("site not ready to migrate to version 4") diff --git a/erpnext/patches/v4_1/__init__.py b/erpnext/patches/v4_1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v4_1/fix_delivery_and_billing_status.py b/erpnext/patches/v4_1/fix_delivery_and_billing_status.py deleted file mode 100644 index 8cc6a4b0be..0000000000 --- a/erpnext/patches/v4_1/fix_delivery_and_billing_status.py +++ /dev/null @@ -1,12 +0,0 @@ -# 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 - -def execute(): - frappe.db.sql("""update `tabSales Order` set delivery_status = 'Not Delivered' - where delivery_status = 'Delivered' and ifnull(per_delivered, 0) = 0 and ifnull(docstatus, 0) in (0, 1)""") - - frappe.db.sql("""update `tabSales Order` set billing_status = 'Not Billed' - where billing_status = 'Billed' and ifnull(per_billed, 0) = 0 and ifnull(docstatus, 0) in (0, 1)""") diff --git a/erpnext/patches/v4_1/fix_jv_remarks.py b/erpnext/patches/v4_1/fix_jv_remarks.py deleted file mode 100644 index e07bf05f1a..0000000000 --- a/erpnext/patches/v4_1/fix_jv_remarks.py +++ /dev/null @@ -1,21 +0,0 @@ -# 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 - -def execute(): - reference_date = guess_reference_date() - for name in frappe.db.sql_list("""select name from `tabJournal Entry` - where date(creation)>=%s""", reference_date): - jv = frappe.get_doc("Journal Entry", name) - try: - jv.create_remarks() - except frappe.MandatoryError: - pass - else: - frappe.db.set_value("Journal Entry", jv.name, "remark", jv.remark) - -def guess_reference_date(): - return (frappe.db.get_value("Patch Log", {"patch": "erpnext.patches.v4_0.validate_v3_patch"}, "creation") - or "2014-05-06") diff --git a/erpnext/patches/v4_1/fix_sales_order_delivered_status.py b/erpnext/patches/v4_1/fix_sales_order_delivered_status.py deleted file mode 100644 index 66037b8958..0000000000 --- a/erpnext/patches/v4_1/fix_sales_order_delivered_status.py +++ /dev/null @@ -1,15 +0,0 @@ -# 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 - -def execute(): - for si in frappe.db.sql_list("""select name - from `tabSales Invoice` - where ifnull(update_stock,0) = 1 and docstatus = 1 and exists( - select name from `tabSales Invoice Item` where parent=`tabSales Invoice`.name and - ifnull(so_detail, "") != "")"""): - - invoice = frappe.get_doc("Sales Invoice", si) - invoice.update_qty() diff --git a/erpnext/patches/v4_1/set_outgoing_email_footer.py b/erpnext/patches/v4_1/set_outgoing_email_footer.py deleted file mode 100644 index 54d016bf5f..0000000000 --- a/erpnext/patches/v4_1/set_outgoing_email_footer.py +++ /dev/null @@ -1,12 +0,0 @@ -# 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 erpnext.setup.install import default_mail_footer - -def execute(): - return - mail_footer = frappe.db.get_default('mail_footer') or '' - mail_footer += default_mail_footer - frappe.db.set_value("Outgoing Email Settings", "Outgoing Email Settings", "footer", mail_footer) diff --git a/erpnext/patches/v4_2/__init__.py b/erpnext/patches/v4_2/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v4_2/add_currency_turkish_lira.py b/erpnext/patches/v4_2/add_currency_turkish_lira.py deleted file mode 100644 index 1a46089f94..0000000000 --- a/erpnext/patches/v4_2/add_currency_turkish_lira.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - -def execute(): - return - # country = get_country_info(country="Turkey") - # add_country_and_currency("Turkey", country) diff --git a/erpnext/patches/v4_2/default_website_style.py b/erpnext/patches/v4_2/default_website_style.py deleted file mode 100644 index e8f9502ea6..0000000000 --- a/erpnext/patches/v4_2/default_website_style.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - return - # frappe.reload_doc('website', 'doctype', 'style_settings') - # style_settings = frappe.get_doc("Style Settings", "Style Settings") - # if not style_settings.apply_style: - # style_settings.update(default_properties) - # style_settings.apply_style = 1 - # style_settings.save() diff --git a/erpnext/patches/v4_2/delete_gl_entries_for_cancelled_invoices.py b/erpnext/patches/v4_2/delete_gl_entries_for_cancelled_invoices.py deleted file mode 100644 index 169b1e2927..0000000000 --- a/erpnext/patches/v4_2/delete_gl_entries_for_cancelled_invoices.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - cancelled_invoices = frappe.db.sql_list("""select name from `tabSales Invoice` - where docstatus = 2 and ifnull(update_stock, 0) = 1""") - - if cancelled_invoices: - frappe.db.sql("""delete from `tabGL Entry` - where voucher_type = 'Sales Invoice' and voucher_no in (%s)""" - % (', '.join(['%s']*len(cancelled_invoices))), tuple(cancelled_invoices)) \ No newline at end of file diff --git a/erpnext/patches/v4_2/delete_old_print_formats.py b/erpnext/patches/v4_2/delete_old_print_formats.py deleted file mode 100644 index cacdb85ce0..0000000000 --- a/erpnext/patches/v4_2/delete_old_print_formats.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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 - -def execute(): - old_formats = ("Sales Invoice", "Sales Invoice Spartan", "Sales Invoice Modern", - "Sales Invoice Classic", - "Sales Order Spartan", "Sales Order Modern", "Sales Order Classic", - "Purchase Order Spartan", "Purchase Order Modern", "Purchase Order Classic", - "Quotation Spartan", "Quotation Modern", "Quotation Classic", - "Delivery Note Spartan", "Delivery Note Modern", "Delivery Note Classic") - - for fmt in old_formats: - # update property setter - for ps in frappe.db.sql_list("""select name from `tabProperty Setter` - where property='default_print_format' and value=%s""", fmt): - ps = frappe.get_doc("Property Setter", ps) - ps.value = "Standard" - ps.save(ignore_permissions = True) - - frappe.delete_doc_if_exists("Print Format", fmt) diff --git a/erpnext/patches/v4_2/discount_amount.py b/erpnext/patches/v4_2/discount_amount.py deleted file mode 100644 index 7ab61bdbea..0000000000 --- a/erpnext/patches/v4_2/discount_amount.py +++ /dev/null @@ -1,12 +0,0 @@ -# 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.modules import scrub, get_doctype_module - -def execute(): - for dt in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]: - frappe.reload_doc(get_doctype_module(dt), "doctype", scrub(dt)) - frappe.db.sql("""update `tab{0}` set base_discount_amount=discount_amount, - discount_amount=discount_amount/conversion_rate""".format(dt)) diff --git a/erpnext/patches/v4_2/fix_account_master_type.py b/erpnext/patches/v4_2/fix_account_master_type.py deleted file mode 100644 index 99444ce83b..0000000000 --- a/erpnext/patches/v4_2/fix_account_master_type.py +++ /dev/null @@ -1,12 +0,0 @@ -# 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 - -def execute(): - for d in frappe.db.sql("""select name from `tabAccount` - where ifnull(master_type, '') not in ('Customer', 'Supplier', 'Employee', '') and docstatus=0"""): - ac = frappe.get_doc("Account", d[0]) - ac.master_type = None - ac.save() diff --git a/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py b/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py deleted file mode 100644 index c6c94d4179..0000000000 --- a/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe -from frappe.utils import flt - -def execute(): - from erpnext.stock.stock_balance import repost - repost(allow_zero_rate=True, only_actual=True) - - frappe.reload_doctype("Account") - - warehouse_account = frappe.db.sql("""select name, master_name from tabAccount - where ifnull(account_type, '') = 'Warehouse'""") - if warehouse_account: - warehouses = [d[1] for d in warehouse_account] - accounts = [d[0] for d in warehouse_account] - - stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no - from `tabStock Ledger Entry` sle - where sle.warehouse in (%s) - order by sle.posting_date""" % - ', '.join(['%s']*len(warehouses)), tuple(warehouses)) - - rejected = [] - for voucher_type, voucher_no in stock_vouchers: - stock_bal = frappe.db.sql("""select sum(stock_value_difference) from `tabStock Ledger Entry` - where voucher_type=%s and voucher_no =%s and warehouse in (%s)""" % - ('%s', '%s', ', '.join(['%s']*len(warehouses))), tuple([voucher_type, voucher_no] + warehouses)) - - account_bal = frappe.db.sql("""select ifnull(sum(ifnull(debit, 0) - ifnull(credit, 0)), 0) - from `tabGL Entry` - where voucher_type=%s and voucher_no =%s and account in (%s) - group by voucher_type, voucher_no""" % - ('%s', '%s', ', '.join(['%s']*len(accounts))), tuple([voucher_type, voucher_no] + accounts)) - - if stock_bal and account_bal and abs(flt(stock_bal[0][0]) - flt(account_bal[0][0])) > 0.1: - try: - print(voucher_type, voucher_no, stock_bal[0][0], account_bal[0][0]) - - frappe.db.sql("""delete from `tabGL Entry` - where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no)) - - voucher = frappe.get_doc(voucher_type, voucher_no) - voucher.make_gl_entries() - frappe.db.commit() - except Exception as e: - print(frappe.get_traceback()) - rejected.append([voucher_type, voucher_no]) - frappe.db.rollback() - - print("Failed to repost: ") - print(rejected) diff --git a/erpnext/patches/v4_2/fix_recurring_orders.py b/erpnext/patches/v4_2/fix_recurring_orders.py deleted file mode 100644 index ea1724a040..0000000000 --- a/erpnext/patches/v4_2/fix_recurring_orders.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - sales_orders = frappe.db.sql("""select name from `tabSales Order` - where docstatus = 1 and ifnull(is_recurring, 0) = 1 - and (per_delivered > 0 or per_billed > 0)""", as_dict=1) - - for so in sales_orders: - if not frappe.db.exists("Delivery Note Item", {"against_sales_order": so.name, "docstatus": 1}): - frappe.db.sql("""update `tabSales Order` set per_delivered = 0, - delivery_status = 'Not Delivered' where name = %s""", so.name) - frappe.db.sql("""update `tabSales Order Item` set delivered_qty = 0 - where parent = %s""", so.name) - - if not frappe.db.exists("Sales Invoice Item", {"sales_order": so.name, "docstatus": 1}): - frappe.db.sql("""update `tabSales Order` set per_billed = 0, - billing_status = 'Not Billed' where name = %s""", so.name) - frappe.db.sql("""update `tabSales Order Item` set billed_amt = 0 - where parent = %s""", so.name) - - purchase_orders = frappe.db.sql("""select name from `tabPurchase Order` - where docstatus = 1 and ifnull(is_recurring, 0) = 1 - and (per_received > 0 or per_billed > 0)""", as_dict=1) - - for po in purchase_orders: - if not frappe.db.exists("Purchase Receipt Item", {"prevdoc_doctype": "Purchase Order", - "prevdoc_docname": po.name, "docstatus": 1}): - frappe.db.sql("""update `tabPurchase Order` set per_received = 0 - where name = %s""", po.name) - frappe.db.sql("""update `tabPurchase Order Item` set received_qty = 0 - where parent = %s""", po.name) - - if not frappe.db.exists("Purchase Invoice Item", {"purchase_order": po.name, "docstatus": 1}): - frappe.db.sql("""update `tabPurchase Order` set per_billed = 0 - where name = %s""", po.name) - frappe.db.sql("""update `tabPurchase Order Item` set billed_amt = 0 - where parent = %s""", po.name) \ No newline at end of file diff --git a/erpnext/patches/v4_2/party_model.py b/erpnext/patches/v4_2/party_model.py deleted file mode 100644 index 46d7fffee1..0000000000 --- a/erpnext/patches/v4_2/party_model.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe - -def execute(): - frappe.reload_doc("accounts", "doctype", "account") - frappe.reload_doc("setup", "doctype", "company") - frappe.reload_doc("accounts", "doctype", "gl_entry") - frappe.reload_doc("accounts", "doctype", "journal_entry_account") - receivable_payable_accounts = create_receivable_payable_account() - if receivable_payable_accounts: - set_party_in_jv_and_gl_entry(receivable_payable_accounts) - delete_individual_party_account() - remove_customer_supplier_account_report() - -def create_receivable_payable_account(): - receivable_payable_accounts = frappe._dict() - - def _create_account(args): - if args["parent_account"] and frappe.db.exists("Account", args["parent_account"]): - account_id = frappe.db.get_value("Account", - {"account_name": args["account_name"], "company": args["company"]}) - if not account_id: - account = frappe.new_doc("Account") - account.is_group = 0 - account.update(args) - account.insert() - - account_id = account.name - - frappe.db.set_value("Company", args["company"], ("default_receivable_account" - if args["account_type"]=="Receivable" else "default_payable_account"), account_id) - - receivable_payable_accounts.setdefault(args["company"], {}).setdefault(args["account_type"], account_id) - - for company in frappe.db.sql_list("select name from tabCompany"): - _create_account({ - "account_name": "Debtors", - "account_type": "Receivable", - "company": company, - "parent_account": get_parent_account(company, "Customer") - }) - - _create_account({ - "account_name": "Creditors", - "account_type": "Payable", - "company": company, - "parent_account": get_parent_account(company, "Supplier") - }) - - return receivable_payable_accounts - -def get_parent_account(company, master_type): - parent_account = None - - if "receivables_group" in frappe.db.get_table_columns("Company"): - parent_account = frappe.get_cached_value('Company', company, - "receivables_group" if master_type=="Customer" else "payables_group") - if not parent_account: - parent_account = frappe.db.get_value("Account", {"company": company, - "account_name": "Accounts Receivable" if master_type=="Customer" else "Accounts Payable"}) - - if not parent_account: - parent_account = frappe.db.sql_list("""select parent_account from tabAccount - where company=%s and ifnull(master_type, '')=%s and ifnull(master_name, '')!='' limit 1""", - (company, master_type)) - parent_account = parent_account[0][0] if parent_account else None - - return parent_account - -def set_party_in_jv_and_gl_entry(receivable_payable_accounts): - accounts = frappe.db.sql("""select name, master_type, master_name, company from `tabAccount` - where ifnull(master_type, '') in ('Customer', 'Supplier') and ifnull(master_name, '') != ''""", as_dict=1) - - account_map = frappe._dict() - for d in accounts: - account_map.setdefault(d.name, d) - - if not account_map: - return - - for dt in ["Journal Entry Account", "GL Entry"]: - records = frappe.db.sql("""select name, account from `tab%s` - where account in (%s) and ifnull(party, '') = '' and docstatus < 2""" % - (dt, ", ".join(['%s']*len(account_map))), tuple(account_map.keys()), as_dict=1) - for i, d in enumerate(records): - account_details = account_map.get(d.account, {}) - account_type = "Receivable" if account_details.get("master_type")=="Customer" else "Payable" - new_account = receivable_payable_accounts[account_details.get("company")][account_type] - - frappe.db.sql("update `tab{0}` set account=%s, party_type=%s, party=%s where name=%s".format(dt), - (new_account, account_details.get("master_type"), account_details.get("master_name"), d.name)) - - if i%500 == 0: - frappe.db.commit() - -def delete_individual_party_account(): - frappe.db.sql("""delete from `tabAccount` - where ifnull(master_type, '') in ('Customer', 'Supplier') - and ifnull(master_name, '') != '' - and not exists(select gle.name from `tabGL Entry` gle - where gle.account = tabAccount.name)""") - - accounts_not_deleted = frappe.db.sql_list("""select tabAccount.name from `tabAccount` - where ifnull(master_type, '') in ('Customer', 'Supplier') - and ifnull(master_name, '') != '' - and exists(select gle.name from `tabGL Entry` gle where gle.account = tabAccount.name)""") - - if accounts_not_deleted: - print("Accounts not deleted: " + "\n".join(accounts_not_deleted)) - - -def remove_customer_supplier_account_report(): - for d in ["Customer Account Head", "Supplier Account Head"]: - frappe.delete_doc("Report", d) diff --git a/erpnext/patches/v4_2/recalculate_bom_cost.py b/erpnext/patches/v4_2/recalculate_bom_cost.py deleted file mode 100644 index eee89fce96..0000000000 --- a/erpnext/patches/v4_2/recalculate_bom_cost.py +++ /dev/null @@ -1,16 +0,0 @@ -# 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 - -def execute(): - for d in frappe.db.sql("select name from `tabBOM` where docstatus < 2"): - try: - document = frappe.get_doc('BOM', d[0]) - if document.docstatus == 1: - document.flags.ignore_validate_update_after_submit = True - document.calculate_cost() - document.save() - except: - pass diff --git a/erpnext/patches/v4_2/repost_sle_for_si_with_no_warehouse.py b/erpnext/patches/v4_2/repost_sle_for_si_with_no_warehouse.py deleted file mode 100644 index 1356129dc0..0000000000 --- a/erpnext/patches/v4_2/repost_sle_for_si_with_no_warehouse.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe -from erpnext.stock.stock_ledger import NegativeStockError - -def execute(): - si_list = frappe.db.sql("""select distinct si.name - from `tabSales Invoice Item` si_item, `tabSales Invoice` si - where si.name = si_item.parent and si.modified > '2015-02-16' and si.docstatus=1 - and ifnull(si_item.warehouse, '') = '' and ifnull(si.update_stock, 0) = 1 - order by posting_date, posting_time""", as_dict=1) - - failed_list = [] - for si in si_list: - try: - si_doc = frappe.get_doc("Sales Invoice", si.name) - si_doc.docstatus = 2 - si_doc.on_cancel() - - si_doc.docstatus = 1 - si_doc.set_missing_item_details() - si_doc.on_submit() - frappe.db.commit() - except: - failed_list.append(si.name) - frappe.local.stockledger_exceptions = None - frappe.db.rollback() - - print("Failed to repost: ", failed_list) - - - \ No newline at end of file diff --git a/erpnext/patches/v4_2/repost_stock_reconciliation.py b/erpnext/patches/v4_2/repost_stock_reconciliation.py deleted file mode 100644 index ad20ebbae4..0000000000 --- a/erpnext/patches/v4_2/repost_stock_reconciliation.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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 -import json - -def execute(): - existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock") - frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) - - head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"] - stock_reco_to_be_reposted = [] - for d in frappe.db.sql("""select name, reconciliation_json from `tabStock Reconciliation` - where docstatus=1 and creation > '2014-03-01'""", as_dict=1): - data = json.loads(d.reconciliation_json) - for row in data[data.index(head_row)+1:]: - if row[3] in ["", None]: - stock_reco_to_be_reposted.append(d.name) - break - - for dn in stock_reco_to_be_reposted: - reco = frappe.get_doc("Stock Reconciliation", dn) - reco.docstatus = 2 - reco.on_cancel() - - reco.docstatus = 1 - reco.validate() - reco.on_submit() - - frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock) diff --git a/erpnext/patches/v4_2/reset_bom_costs.py b/erpnext/patches/v4_2/reset_bom_costs.py deleted file mode 100644 index 42ca759467..0000000000 --- a/erpnext/patches/v4_2/reset_bom_costs.py +++ /dev/null @@ -1,17 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doc('manufacturing', 'doctype', 'bom_operation') - for d in frappe.db.sql("""select name from `tabBOM` where docstatus < 2""", as_dict=1): - try: - bom = frappe.get_doc('BOM', d.name) - bom.flags.ignore_validate_update_after_submit = True - bom.calculate_cost() - bom.save() - frappe.db.commit() - except: - frappe.db.rollback() diff --git a/erpnext/patches/v4_2/seprate_manufacture_and_repack.py b/erpnext/patches/v4_2/seprate_manufacture_and_repack.py deleted file mode 100644 index 2c935436a2..0000000000 --- a/erpnext/patches/v4_2/seprate_manufacture_and_repack.py +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - -def execute(): - frappe.db.sql("""update `tabStock Entry` set purpose='Manufacture' where purpose='Manufacture/Repack' and ifnull(work_order,"")!="" """) - frappe.db.sql("""update `tabStock Entry` set purpose='Repack' where purpose='Manufacture/Repack' and ifnull(work_order,"")="" """) \ No newline at end of file diff --git a/erpnext/patches/v4_2/set_company_country.py b/erpnext/patches/v4_2/set_company_country.py deleted file mode 100644 index 89f07f2873..0000000000 --- a/erpnext/patches/v4_2/set_company_country.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe - -def execute(): - country = frappe.db.get_single_value("Global Defaults", "country") - if not country: - print("Country not specified in Global Defaults") - return - - for company in frappe.db.sql_list("""select name from `tabCompany` - where ifnull(country, '')=''"""): - frappe.db.set_value("Company", company, "country", country) diff --git a/erpnext/patches/v4_2/set_item_has_batch.py b/erpnext/patches/v4_2/set_item_has_batch.py deleted file mode 100644 index 7e52d2def0..0000000000 --- a/erpnext/patches/v4_2/set_item_has_batch.py +++ /dev/null @@ -1,65 +0,0 @@ -# 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 - -def execute(): - frappe.db.sql("update tabItem set has_batch_no = 0 where ifnull(has_batch_no, '') = ''") - frappe.db.sql("update tabItem set has_serial_no = 0 where ifnull(has_serial_no, '') = ''") - - item_list = frappe.db.sql("""select name, has_batch_no, has_serial_no from tabItem - where is_stock_item = 1""", as_dict=1) - - sle_count = get_sle_count() - sle_with_batch = get_sle_with_batch() - sle_with_serial = get_sle_with_serial() - - batch_items = get_items_with_batch() - serialized_items = get_items_with_serial() - - for d in item_list: - if d.has_batch_no == 1: - if d.name not in batch_items and sle_count.get(d.name) and sle_count.get(d.name) != sle_with_batch.get(d.name): - frappe.db.set_value("Item", d.name, "has_batch_no", 0) - else: - if d.name in batch_items or (sle_count.get(d.name) and sle_count.get(d.name) == sle_with_batch.get(d.name)): - frappe.db.set_value("Item", d.name, "has_batch_no", 1) - - if d.has_serial_no == 1: - if d.name not in serialized_items and sle_count.get(d.name) and sle_count.get(d.name) != sle_with_serial.get(d.name): - frappe.db.set_value("Item", d.name, "has_serial_no", 0) - else: - if d.name in serialized_items or (sle_count.get(d.name) and sle_count.get(d.name) == sle_with_serial.get(d.name)): - frappe.db.set_value("Item", d.name, "has_serial_no", 1) - - -def get_sle_count(): - sle_count = {} - for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry` group by item_code""", as_dict=1): - sle_count.setdefault(d.item_code, d.cnt) - - return sle_count - -def get_sle_with_batch(): - sle_with_batch = {} - for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry` - where ifnull(batch_no, '') != '' group by item_code""", as_dict=1): - sle_with_batch.setdefault(d.item_code, d.cnt) - - return sle_with_batch - - -def get_sle_with_serial(): - sle_with_serial = {} - for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry` - where ifnull(serial_no, '') != '' group by item_code""", as_dict=1): - sle_with_serial.setdefault(d.item_code, d.cnt) - - return sle_with_serial - -def get_items_with_batch(): - return frappe.db.sql_list("select item from tabBatch") - -def get_items_with_serial(): - return frappe.db.sql_list("select item_code from `tabSerial No`") diff --git a/erpnext/patches/v4_2/toggle_rounded_total.py b/erpnext/patches/v4_2/toggle_rounded_total.py deleted file mode 100644 index e571208eb6..0000000000 --- a/erpnext/patches/v4_2/toggle_rounded_total.py +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - -def execute(): - global_defaults = frappe.get_doc("Global Defaults", "Global Defaults") - global_defaults.toggle_rounded_total() diff --git a/erpnext/patches/v4_2/update_landed_cost_voucher.py b/erpnext/patches/v4_2/update_landed_cost_voucher.py deleted file mode 100644 index ec0029671e..0000000000 --- a/erpnext/patches/v4_2/update_landed_cost_voucher.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doc("stock", "doctype", "landed_cost_voucher") - frappe.db.sql("""update `tabLanded Cost Voucher` set distribute_charges_based_on = 'Amount' - where docstatus=1""") diff --git a/erpnext/patches/v4_2/update_project_milestones.py b/erpnext/patches/v4_2/update_project_milestones.py deleted file mode 100644 index e704116d05..0000000000 --- a/erpnext/patches/v4_2/update_project_milestones.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - for project in frappe.db.sql_list("select name from tabProject"): - frappe.reload_doc("projects", "doctype", "project") - p = frappe.get_doc("Project", project) - p.update_milestones_completed() - p.db_set("percent_milestones_completed", p.percent_milestones_completed) diff --git a/erpnext/patches/v4_2/update_sales_order_invoice_field_name.py b/erpnext/patches/v4_2/update_sales_order_invoice_field_name.py deleted file mode 100644 index 28dd5c0d4e..0000000000 --- a/erpnext/patches/v4_2/update_sales_order_invoice_field_name.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('accounts', 'doctype', 'sales_invoice') - frappe.db.sql("""update `tabSales Invoice` set from_date = invoice_period_from_date, - to_date = invoice_period_to_date, is_recurring = convert_into_recurring_invoice""") diff --git a/erpnext/patches/v4_2/update_stock_uom_for_dn_in_sle.py b/erpnext/patches/v4_2/update_stock_uom_for_dn_in_sle.py deleted file mode 100644 index 89bf795534..0000000000 --- a/erpnext/patches/v4_2/update_stock_uom_for_dn_in_sle.py +++ /dev/null @@ -1,11 +0,0 @@ -# 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 - -def execute(): - frappe.db.sql("""update `tabStock Ledger Entry` sle, tabItem item - set sle.stock_uom = item.stock_uom - where sle.voucher_type="Delivery Note" and item.name = sle.item_code - and sle.stock_uom != item.stock_uom""") diff --git a/erpnext/patches/v4_4/__init__.py b/erpnext/patches/v4_4/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v4_4/make_email_accounts.py b/erpnext/patches/v4_4/make_email_accounts.py deleted file mode 100644 index 57df1ae491..0000000000 --- a/erpnext/patches/v4_4/make_email_accounts.py +++ /dev/null @@ -1,96 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model import default_fields - -from six import iteritems - -def execute(): - frappe.reload_doc("email", "doctype", "email_account") - - # outgoing - outgoing = dict(frappe.db.sql("select field, value from tabSingles where doctype='Outgoing Email Settings'")) - if outgoing and outgoing['mail_server'] and outgoing['mail_login']: - account = frappe.new_doc("Email Account") - mapping = { - "login_id_is_different": 1, - "email_id": "auto_email_id", - "login_id": "mail_login", - "password": "mail_password", - "footer": "footer", - "smtp_server": "mail_server", - "smtp_port": "mail_port", - "use_tls": "use_ssl" - } - - for target_fieldname, source_fieldname in iteritems(mapping): - account.set(target_fieldname, outgoing.get(source_fieldname)) - - account.enable_outgoing = 1 - account.enable_incoming = 0 - - account.insert() - - # support - support = dict(frappe.db.sql("select field, value from tabSingles where doctype='Support Email Settings'")) - if support and support['mail_server'] and support['mail_login']: - account = frappe.new_doc("Email Account") - mapping = { - "enable_incoming": "sync_support_mails", - "email_id": "mail_login", - "password": "mail_password", - "email_server": "mail_server", - "use_ssl": "use_ssl", - "signature": "support_signature", - "enable_auto_reply": "send_autoreply", - "auto_reply_message": "support_autoreply" - } - - for target_fieldname, source_fieldname in iteritems(mapping): - account.set(target_fieldname, support.get(source_fieldname)) - - account.enable_outgoing = 0 - account.append_to = "Issue" - - insert_or_update(account) - - # sales, jobs - for doctype in ("Sales Email Settings", "Jobs Email Settings"): - source = dict(frappe.db.sql("select field, value from tabSingles where doctype=%s", doctype)) - if source and source.get('host') and source.get('username'): - account = frappe.new_doc("Email Account") - mapping = { - "enable_incoming": "extract_emails", - "email_id": "username", - "password": "password", - "email_server": "host", - "use_ssl": "use_ssl", - } - - for target_fieldname, source_fieldname in iteritems(mapping): - account.set(target_fieldname, source.get(source_fieldname)) - - account.enable_outgoing = 0 - account.append_to = "Lead" if doctype=="Sales Email Settings" else "Job Applicant" - - insert_or_update(account) - - for doctype in ("Outgoing Email Settings", "Support Email Settings", - "Sales Email Settings", "Jobs Email Settings"): - frappe.delete_doc("DocType", doctype) - -def insert_or_update(account): - try: - account.insert() - except frappe.NameError as e: - if e.args[0]=="Email Account": - existing_account = frappe.get_doc("Email Account", e.args[1]) - for key, value in account.as_dict().items(): - if not existing_account.get(key) and value \ - and key not in default_fields \ - and key != "__islocal": - existing_account.set(key, value) - - existing_account.save() - else: - raise - diff --git a/erpnext/patches/v5_0/__init__.py b/erpnext/patches/v5_0/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v5_0/convert_stock_reconciliation.py b/erpnext/patches/v5_0/convert_stock_reconciliation.py deleted file mode 100644 index 75d1da752f..0000000000 --- a/erpnext/patches/v5_0/convert_stock_reconciliation.py +++ /dev/null @@ -1,31 +0,0 @@ -from __future__ import unicode_literals -import frappe, json - -def execute(): - # stock reco now amendable - frappe.db.sql("""update tabDocPerm set `amend` = 1 where parent='Stock Reconciliation' and submit = 1""") - - frappe.reload_doc("stock", "doctype", "stock_reconciliation_item") - frappe.reload_doctype("Stock Reconciliation") - - if frappe.db.has_column("Stock Reconciliation", "reconciliation_json"): - for sr in frappe.db.get_all("Stock Reconciliation", ["name"], - {"reconciliation_json": ["!=", ""]}): - start = False - sr = frappe.get_doc("Stock Reconciliation", sr.name) - for row in json.loads(sr.reconciliation_json): - if start: - sr.append("items", { - "item_code": row[0], - "warehouse": row[1], - "qty": row[2] if len(row) > 2 else None, - "valuation_rate": row[3] if len(row) > 3 else None - }) - - elif row[0]=="Item Code": - start = True - - - for item in sr.items: - item.db_update() - diff --git a/erpnext/patches/v5_0/execute_on_doctype_update.py b/erpnext/patches/v5_0/execute_on_doctype_update.py deleted file mode 100644 index 70b1d8ded6..0000000000 --- a/erpnext/patches/v5_0/execute_on_doctype_update.py +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - -def execute(): - for dt in ("Stock Ledger Entry", "Communication", "DefaultValue", "DocShare", "File", "ToDo"): - frappe.get_doc("DocType", dt).run_module_method("on_doctype_update") diff --git a/erpnext/patches/v5_0/fix_taxes_and_totals_in_party_currency.py b/erpnext/patches/v5_0/fix_taxes_and_totals_in_party_currency.py deleted file mode 100644 index 30dc0f8db4..0000000000 --- a/erpnext/patches/v5_0/fix_taxes_and_totals_in_party_currency.py +++ /dev/null @@ -1,66 +0,0 @@ -# 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.model.meta import get_field_precision - -def execute(): - if not frappe.db.sql("""select name from `tabPatch Log` - where patch = 'erpnext.patches.v5_0.taxes_and_totals_in_party_currency'"""): - return - selling_doctypes = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"] - buying_doctypes = ["Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"] - - for dt in selling_doctypes: - update_values(dt, "Sales Taxes and Charges") - - for dt in buying_doctypes: - update_values(dt, "Purchase Taxes and Charges") - -def update_values(dt, tax_table): - rate_field_precision = get_field_precision(frappe.get_meta(dt + " Item").get_field("rate")) - tax_amount_precision = get_field_precision(frappe.get_meta(tax_table).get_field("tax_amount")) - - # update net_total, discount_on - frappe.db.sql(""" - UPDATE - `tab{0}` - SET - total_taxes_and_charges = round(base_total_taxes_and_charges / conversion_rate, {1}) - WHERE - docstatus < 2 - and ifnull(base_total_taxes_and_charges, 0) != 0 - and ifnull(total_taxes_and_charges, 0) = 0 - """.format(dt, tax_amount_precision)) - - # update net_amount - frappe.db.sql(""" - UPDATE - `tab{0}` par, `tab{1}` item - SET - item.net_amount = round(item.base_net_amount / par.conversion_rate, {2}), - item.net_rate = round(item.base_net_rate / par.conversion_rate, {2}) - WHERE - par.name = item.parent - and par.docstatus < 2 - and ((ifnull(item.base_net_amount, 0) != 0 and ifnull(item.net_amount, 0) = 0) - or (ifnull(item.base_net_rate, 0) != 0 and ifnull(item.net_rate, 0) = 0)) - """.format(dt, dt + " Item", rate_field_precision)) - - # update tax in party currency - frappe.db.sql(""" - UPDATE - `tab{0}` par, `tab{1}` tax - SET - tax.tax_amount = round(tax.base_tax_amount / par.conversion_rate, {2}), - tax.total = round(tax.base_total / conversion_rate, {2}), - tax.tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount / conversion_rate, {2}) - WHERE - par.name = tax.parent - and par.docstatus < 2 - and ((ifnull(tax.base_tax_amount, 0) != 0 and ifnull(tax.tax_amount, 0) = 0) - or (ifnull(tax.base_total, 0) != 0 and ifnull(tax.total, 0) = 0) - or (ifnull(tax.base_tax_amount_after_discount_amount, 0) != 0 and - ifnull(tax.tax_amount_after_discount_amount, 0) = 0)) - """.format(dt, tax_table, tax_amount_precision)) \ No newline at end of file diff --git a/erpnext/patches/v5_0/index_on_account_and_gl_entry.py b/erpnext/patches/v5_0/index_on_account_and_gl_entry.py deleted file mode 100644 index 2920e9293d..0000000000 --- a/erpnext/patches/v5_0/index_on_account_and_gl_entry.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import unicode_literals - -import frappe - -def execute(): - index_map = { - "Account": ["parent_account", "lft", "rgt"], - "GL Entry": ["posting_date", "account", 'party', "voucher_no"], - "Sales Invoice": ["posting_date", "debit_to", "customer"], - "Purchase Invoice": ["posting_date", "credit_to", "supplier"] - } - - for dt, indexes in index_map.items(): - existing_indexes = [(d.Key_name, d.Column_name) for d in frappe.db.sql("""show index from `tab{0}` - where Column_name != 'name'""".format(dt), as_dict=1)] - - for old, column in existing_indexes: - if column in ("parent", "group_or_ledger", "is_group", "is_pl_account", "debit_or_credit", - "account_name", "company", "project", "voucher_date", "due_date", "bill_no", - "bill_date", "is_opening", "fiscal_year", "outstanding_amount"): - frappe.db.sql("alter table `tab{0}` drop index {1}".format(dt, old)) - - existing_indexes = [(d.Key_name, d.Column_name) for d in frappe.db.sql("""show index from `tab{0}` - where Column_name != 'name'""".format(dt), as_dict=1)] - - existing_indexed_columns = list(set([x[1] for x in existing_indexes])) - - for new in indexes: - if new not in existing_indexed_columns: - frappe.db.sql("alter table `tab{0}` add index ({1})".format(dt, new)) \ No newline at end of file diff --git a/erpnext/patches/v5_0/is_group.py b/erpnext/patches/v5_0/is_group.py deleted file mode 100644 index 4e3f760bed..0000000000 --- a/erpnext/patches/v5_0/is_group.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals - -import frappe - -def execute(): - frappe.reload_doctype("Account") - frappe.reload_doctype("Cost Center") - frappe.db.sql("update tabAccount set is_group = if(group_or_ledger='Group', 1, 0)") - frappe.db.sql("update `tabCost Center` set is_group = if(group_or_ledger='Group', 1, 0)") diff --git a/erpnext/patches/v5_0/item_patches.py b/erpnext/patches/v5_0/item_patches.py deleted file mode 100644 index e223e09f5b..0000000000 --- a/erpnext/patches/v5_0/item_patches.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.db.sql("update `tabItem` set end_of_life='2099-12-31' where ifnull(end_of_life, '0000-00-00')='0000-00-00'") - frappe.db.sql("update `tabItem` set website_image = image where ifnull(website_image, '') = 'attach_files:'") diff --git a/erpnext/patches/v5_0/link_warehouse_with_account.py b/erpnext/patches/v5_0/link_warehouse_with_account.py deleted file mode 100644 index 338fd7ad7f..0000000000 --- a/erpnext/patches/v5_0/link_warehouse_with_account.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - -def execute(): - if "master_name" in frappe.db.get_table_columns("Account"): - frappe.db.sql("""update tabAccount set warehouse=master_name - where ifnull(account_type, '') = 'Warehouse' and ifnull(master_name, '') != ''""") \ No newline at end of file diff --git a/erpnext/patches/v5_0/new_crm_module.py b/erpnext/patches/v5_0/new_crm_module.py deleted file mode 100644 index f5dda1f273..0000000000 --- a/erpnext/patches/v5_0/new_crm_module.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import json -import frappe - -def execute(): - frappe.reload_doc('crm', 'doctype', 'lead') - frappe.reload_doc('crm', 'doctype', 'opportunity') - - add_crm_to_user_desktop_items() - -def add_crm_to_user_desktop_items(): - key = "_user_desktop_items" - for user in frappe.get_all("User", filters={"enabled": 1, "user_type": "System User"}): - user = user.name - user_desktop_items = frappe.db.get_defaults(key, parent=user) - if user_desktop_items: - user_desktop_items = json.loads(user_desktop_items) - if "CRM" not in user_desktop_items: - user_desktop_items.append("CRM") - frappe.db.set_default(key, json.dumps(user_desktop_items), parent=user) - diff --git a/erpnext/patches/v5_0/newsletter.py b/erpnext/patches/v5_0/newsletter.py deleted file mode 100644 index 63e3312413..0000000000 --- a/erpnext/patches/v5_0/newsletter.py +++ /dev/null @@ -1,38 +0,0 @@ -# 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 -import frappe.permissions - -def execute(): - frappe.reload_doc("core", "doctype", "block_module") - frappe.reload_doctype("User") - frappe.reload_doctype("Lead") - frappe.reload_doctype("Contact") - - frappe.reload_doc('email', 'doctype', 'email_group') - frappe.reload_doc('email', 'doctype', 'email_group_member') - frappe.reload_doc('email', 'doctype', 'newsletter') - - frappe.permissions.reset_perms("Newsletter") - - if not frappe.db.exists("Role", "Newsletter Manager"): - frappe.get_doc({"doctype": "Role", "role": "Newsletter Manager"}).insert() - - for userrole in frappe.get_all("Has Role", "parent", {"role": "Sales Manager", "parenttype": "User"}): - if frappe.db.exists("User", userrole.parent): - user = frappe.get_doc("User", userrole.parent) - user.append("roles", { - "doctype": "Has Role", - "role": "Newsletter Manager" - }) - user.flags.ignore_mandatory = True - user.save() - - # create default lists - general = frappe.new_doc("Email Group") - general.title = "General" - general.insert() - general.import_from("Lead") - general.import_from("Contact") diff --git a/erpnext/patches/v5_0/opportunity_not_submittable.py b/erpnext/patches/v5_0/opportunity_not_submittable.py deleted file mode 100644 index e29d66f284..0000000000 --- a/erpnext/patches/v5_0/opportunity_not_submittable.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doctype("Opportunity") - frappe.db.sql("update tabDocPerm set submit=0, cancel=0, amend=0 where parent='Opportunity'") - frappe.db.sql("update tabOpportunity set docstatus=0 where docstatus=1") diff --git a/erpnext/patches/v5_0/party_model_patch_fix.py b/erpnext/patches/v5_0/party_model_patch_fix.py deleted file mode 100644 index d9b6709792..0000000000 --- a/erpnext/patches/v5_0/party_model_patch_fix.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - for company in frappe.get_all("Company", - ["name", "default_receivable_account", "default_payable_account"]): - - if company.default_receivable_account: - frappe.db.sql("""update `tabSales Invoice` invoice set `debit_to`=%(account)s - where company=%(company)s - and not exists (select name from `tabAccount` account where account.name=invoice.debit_to)""", - {"company": company.name, "account": company.default_receivable_account}) - - if company.default_payable_account: - frappe.db.sql("""update `tabPurchase Invoice` invoice set `credit_to`=%(account)s - where company=%(company)s - and not exists (select name from `tabAccount` account where account.name=invoice.credit_to)""", - {"company": company.name, "account": company.default_payable_account}) diff --git a/erpnext/patches/v5_0/portal_fixes.py b/erpnext/patches/v5_0/portal_fixes.py deleted file mode 100644 index 1fefd99167..0000000000 --- a/erpnext/patches/v5_0/portal_fixes.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import unicode_literals -import frappe -import erpnext.setup.install - -def execute(): - frappe.reload_doc("website", "doctype", "web_form_field", force=True, reset_permissions=True) - #erpnext.setup.install.add_web_forms() diff --git a/erpnext/patches/v5_0/project_costing.py b/erpnext/patches/v5_0/project_costing.py deleted file mode 100644 index e2d65d0594..0000000000 --- a/erpnext/patches/v5_0/project_costing.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Project") - frappe.db.sql("update `tabProject` set expected_start_date = project_start_date, \ - expected_end_date = completion_date, actual_end_date = act_completion_date, \ - estimated_costing = project_value, gross_margin = gross_margin_value") \ No newline at end of file diff --git a/erpnext/patches/v5_0/recalculate_total_amount_in_jv.py b/erpnext/patches/v5_0/recalculate_total_amount_in_jv.py deleted file mode 100644 index d5af43c541..0000000000 --- a/erpnext/patches/v5_0/recalculate_total_amount_in_jv.py +++ /dev/null @@ -1,26 +0,0 @@ -# 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 import money_in_words - -def execute(): - company_currency = dict(frappe.db.sql("select name, default_currency from `tabCompany`")) - bank_or_cash_accounts = frappe.db.sql_list("""select name from `tabAccount` - where account_type in ('Bank', 'Cash') and docstatus < 2""") - - for je in frappe.db.sql_list("""select name from `tabJournal Entry` where docstatus < 2"""): - total_amount = 0 - total_amount_in_words = "" - - je_doc = frappe.get_doc('Journal Entry', je) - for d in je_doc.get("accounts"): - if (d.party_type and d.party) or d.account in bank_or_cash_accounts: - total_amount = d.debit or d.credit - if total_amount: - total_amount_in_words = money_in_words(total_amount, company_currency.get(je_doc.company)) - - if total_amount: - frappe.db.sql("""update `tabJournal Entry` set total_amount=%s, total_amount_in_words=%s - where name = %s""", (total_amount, total_amount_in_words, je)) diff --git a/erpnext/patches/v5_0/reclculate_planned_operating_cost_in_production_order.py b/erpnext/patches/v5_0/reclculate_planned_operating_cost_in_production_order.py deleted file mode 100644 index 6d392831b4..0000000000 --- a/erpnext/patches/v5_0/reclculate_planned_operating_cost_in_production_order.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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 - -def execute(): - for wo in frappe.db.sql("""select name from `tabWork Order` where docstatus < 2""", as_dict=1): - work_order = frappe.get_doc("Work Order", wo.name) - if work_order.operations: - work_order.flags.ignore_validate_update_after_submit = True - work_order.calculate_time() - work_order.save() \ No newline at end of file diff --git a/erpnext/patches/v5_0/remove_birthday_events.py b/erpnext/patches/v5_0/remove_birthday_events.py deleted file mode 100644 index 3ead8669b8..0000000000 --- a/erpnext/patches/v5_0/remove_birthday_events.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - for e in frappe.db.sql_list("""select name from tabEvent where - repeat_on='Every Year' and ref_type='Employee'"""): - frappe.delete_doc("Event", e, force=True) diff --git a/erpnext/patches/v5_0/rename_customer_issue.py b/erpnext/patches/v5_0/rename_customer_issue.py deleted file mode 100644 index 1bd69ceec1..0000000000 --- a/erpnext/patches/v5_0/rename_customer_issue.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.table_exists("Customer Issue"): - frappe.rename_doc("DocType", "Customer Issue", "Warranty Claim") diff --git a/erpnext/patches/v5_0/rename_pos_setting.py b/erpnext/patches/v5_0/rename_pos_setting.py deleted file mode 100644 index bf10333564..0000000000 --- a/erpnext/patches/v5_0/rename_pos_setting.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.table_exists("POS Setting"): - frappe.rename_doc("DocType", "POS Setting", "POS Profile") diff --git a/erpnext/patches/v5_0/rename_table_fieldnames.py b/erpnext/patches/v5_0/rename_table_fieldnames.py deleted file mode 100644 index aefb0a2037..0000000000 --- a/erpnext/patches/v5_0/rename_table_fieldnames.py +++ /dev/null @@ -1,243 +0,0 @@ -# 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.model.utils.rename_field import rename_field -from frappe.modules import scrub, get_doctype_module - -rename_map = { - "Opportunity": [ - ["enquiry_details", "items"] - ], - "Quotation": [ - ["quotation_details", "items"], - ["other_charges", "taxes"] - ], - "Sales Order": [ - ["sales_order_details", "items"], - ["other_charges", "taxes"], - ["packing_details", "packed_items"] - ], - "Delivery Note": [ - ["delivery_note_details", "items"], - ["other_charges", "taxes"], - ["packing_details", "packed_items"] - ], - "Sales Invoice": [ - ["entries", "items"], - ["other_charges", "taxes"], - ["packing_details", "packed_items"], - ["advance_adjustment_details", "advances"] - ], - "Material Request": [ - ["indent_details", "items"] - ], - "Supplier Quotation": [ - ["quotation_items", "items"], - ["other_charges", "taxes"] - ], - "Purchase Order": [ - ["po_details", "items"], - ["other_charges", "taxes"], - ["po_raw_material_details", "supplied_items"] - ], - "Purchase Receipt": [ - ["purchase_receipt_details", "items"], - ["other_charges", "taxes"], - ["pr_raw_material_details", "supplied_items"] - ], - "Purchase Invoice": [ - ["entries", "items"], - ["other_charges", "taxes"], - ["advance_allocation_details", "advances"] - ], - "Work Order": [ - ["production_order_operations", "operations"] - ], - "BOM": [ - ["bom_operations", "operations"], - ["bom_materials", "items"], - ["flat_bom_details", "exploded_items"] - ], - "Payment Reconciliation": [ - ["payment_reconciliation_payments", "payments"], - ["payment_reconciliation_invoices", "invoices"] - ], - "Sales Taxes and Charges Template": [ - ["other_charges", "taxes"], - ["valid_for_territories", "territories"] - ], - "Purchase Taxes and Charges Template": [ - ["other_charges", "taxes"] - ], - "Shipping Rule": [ - ["shipping_rule_conditions", "conditions"], - ["valid_for_territories", "territories"] - ], - "Price List": [ - ["valid_for_territories", "territories"] - ], - "Appraisal": [ - ["appraisal_details", "goals"] - ], - "Appraisal Template": [ - ["kra_sheet", "goals"] - ], - "Bank Reconciliation": [ - ["entries", "journal_entries"] - ], - "C-Form": [ - ["invoice_details", "invoices"] - ], - "Employee": [ - ["employee_leave_approvers", "leave_approvers"], - ["educational_qualification_details", "education"], - ["previous_experience_details", "external_work_history"], - ["experience_in_company_details", "internal_work_history"] - ], - "Expense Claim": [ - ["expense_voucher_details", "expenses"] - ], - "Fiscal Year": [ - ["fiscal_year_companies", "companies"] - ], - "Holiday List": [ - ["holiday_list_details", "holidays"] - ], - "Installation Note": [ - ["installed_item_details", "items"] - ], - "Item": [ - ["item_reorder", "reorder_levels"], - ["uom_conversion_details", "uoms"], - ["item_supplier_details", "supplier_items"], - ["item_customer_details", "customer_items"], - ["item_tax", "taxes"], - ["item_specification_details", "quality_parameters"], - ["item_website_specifications", "website_specifications"] - ], - "Item Group": [ - ["item_website_specifications", "website_specifications"] - ], - "Landed Cost Voucher": [ - ["landed_cost_purchase_receipts", "purchase_receipts"], - ["landed_cost_items", "items"], - ["landed_cost_taxes_and_charges", "taxes"] - ], - "Maintenance Schedule": [ - ["item_maintenance_detail", "items"], - ["maintenance_schedule_detail", "schedules"] - ], - "Maintenance Visit": [ - ["maintenance_visit_details", "purposes"] - ], - "Packing Slip": [ - ["item_details", "items"] - ], - "Customer": [ - ["party_accounts", "accounts"] - ], - "Customer Group": [ - ["party_accounts", "accounts"] - ], - "Supplier": [ - ["party_accounts", "accounts"] - ], - "Supplier Type": [ - ["party_accounts", "accounts"] - ], - "Payment Tool": [ - ["payment_tool_details", "vouchers"] - ], - "Production Planning Tool": [ - ["pp_so_details", "sales_orders"], - ["pp_details", "items"] - ], - "Quality Inspection": [ - ["qa_specification_details", "readings"] - ], - "Salary Slip": [ - ["earning_details", "earnings"], - ["deduction_details", "deductions"] - ], - "Salary Structure": [ - ["earning_details", "earnings"], - ["deduction_details", "deductions"] - ], - "Product Bundle": [ - ["sales_bom_items", "items"] - ], - "SMS Settings": [ - ["static_parameter_details", "parameters"] - ], - "Stock Entry": [ - ["mtn_details", "items"] - ], - "Sales Partner": [ - ["partner_target_details", "targets"] - ], - "Sales Person": [ - ["target_details", "targets"] - ], - "Territory": [ - ["target_details", "targets"] - ], - "Time Sheet": [ - ["time_sheet_details", "time_logs"] - ], - "Workstation": [ - ["workstation_operation_hours", "working_hours"] - ], - "Payment Reconciliation Payment": [ - ["journal_voucher", "journal_entry"], - ], - "Purchase Invoice Advance": [ - ["journal_voucher", "journal_entry"], - ], - "Sales Invoice Advance": [ - ["journal_voucher", "journal_entry"], - ], - "Journal Entry": [ - ["entries", "accounts"] - ], - "Monthly Distribution": [ - ["budget_distribution_details", "percentages"] - ] -} - -def execute(): - # rename doctypes - tables = frappe.db.sql_list("show tables") - for old_dt, new_dt in [["Journal Voucher Detail", "Journal Entry Account"], - ["Journal Voucher", "Journal Entry"], - ["Budget Distribution Detail", "Monthly Distribution Percentage"], - ["Budget Distribution", "Monthly Distribution"]]: - if "tab"+new_dt not in tables: - frappe.rename_doc("DocType", old_dt, new_dt, force=True) - - # reload new child doctypes - frappe.reload_doc("manufacturing", "doctype", "work_order_operation") - frappe.reload_doc("manufacturing", "doctype", "workstation_working_hour") - frappe.reload_doc("stock", "doctype", "item_variant") - frappe.reload_doc("Payroll", "doctype", "salary_detail") - frappe.reload_doc("accounts", "doctype", "party_account") - frappe.reload_doc("accounts", "doctype", "fiscal_year_company") - - #rename table fieldnames - for dn in rename_map: - if not frappe.db.exists("DocType", dn): - continue - frappe.reload_doc(get_doctype_module(dn), "doctype", scrub(dn)) - - for dt, field_list in rename_map.items(): - if not frappe.db.exists("DocType", dt): - continue - for field in field_list: - rename_field(dt, field[0], field[1]) - - # update voucher type - for old, new in [["Bank Voucher", "Bank Entry"], ["Cash Voucher", "Cash Entry"], - ["Credit Card Voucher", "Credit Card Entry"], ["Contra Voucher", "Contra Entry"], - ["Write Off Voucher", "Write Off Entry"], ["Excise Voucher", "Excise Entry"]]: - frappe.db.sql("update `tabJournal Entry` set voucher_type=%s where voucher_type=%s", (new, old)) diff --git a/erpnext/patches/v5_0/rename_taxes_and_charges_master.py b/erpnext/patches/v5_0/rename_taxes_and_charges_master.py deleted file mode 100644 index e26f48cda1..0000000000 --- a/erpnext/patches/v5_0/rename_taxes_and_charges_master.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import unicode_literals -import frappe - - -def execute(): - if frappe.db.table_exists("Sales Taxes and Charges Master"): - frappe.rename_doc("DocType", "Sales Taxes and Charges Master", - "Sales Taxes and Charges Template") - frappe.delete_doc("DocType", "Sales Taxes and Charges Master") - - if frappe.db.table_exists("Purchase Taxes and Charges Master"): - frappe.rename_doc("DocType", "Purchase Taxes and Charges Master", - "Purchase Taxes and Charges Template") - frappe.delete_doc("DocType", "Purchase Taxes and Charges Master") diff --git a/erpnext/patches/v5_0/rename_total_fields.py b/erpnext/patches/v5_0/rename_total_fields.py deleted file mode 100644 index 6657dd843e..0000000000 --- a/erpnext/patches/v5_0/rename_total_fields.py +++ /dev/null @@ -1,55 +0,0 @@ -# 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.model.utils.rename_field import rename_field -from frappe.modules import scrub, get_doctype_module - -selling_doctypes = ("Quotation", "Sales Order", "Delivery Note", "Sales Invoice") - -buying_doctypes = ("Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice") - -selling_renamed_fields = ( - ("net_total", "base_net_total"), - ("net_total_export", "net_total"), - ("other_charges_total", "base_total_taxes_and_charges"), - ("other_charges_total_export", "total_taxes_and_charges"), - ("grand_total", "base_grand_total"), - ("grand_total_export", "grand_total"), - ("rounded_total", "base_rounded_total"), - ("rounded_total_export", "rounded_total"), - ("in_words", "base_in_words"), - ("in_words_export", "in_words") -) - -buying_renamed_fields = ( - ("net_total", "base_net_total"), - ("net_total_import", "net_total"), - ("grand_total", "base_grand_total"), - ("grand_total_import", "grand_total"), - ("rounded_total", "base_rounded_total"), - ("in_words", "base_in_words"), - ("in_words_import", "in_words"), - ("other_charges_added", "base_taxes_and_charges_added"), - ("other_charges_added_import", "taxes_and_charges_added"), - ("other_charges_deducted", "base_taxes_and_charges_deducted"), - ("other_charges_deducted_import", "taxes_and_charges_deducted"), - ("total_tax", "base_total_taxes_and_charges") -) - -def execute(): - for doctypes, fields in [[selling_doctypes, selling_renamed_fields], [buying_doctypes, buying_renamed_fields]]: - for dt in doctypes: - frappe.reload_doc(get_doctype_module(dt), "doctype", scrub(dt)) - table_columns = frappe.db.get_table_columns(dt) - base_net_total = frappe.db.sql("select sum(ifnull({0}, 0)) from `tab{1}`".format(fields[0][1], dt))[0][0] - if not base_net_total: - for f in fields: - if f[0] in table_columns: - rename_field(dt, f[0], f[1]) - - # Added new field "total_taxes_and_charges" in buying cycle, updating value - if dt in ("Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"): - frappe.db.sql("""update `tab{0}` set total_taxes_and_charges = - round(base_total_taxes_and_charges/conversion_rate, 2)""".format(dt)) 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 deleted file mode 100644 index c564f8b02a..0000000000 --- a/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py +++ /dev/null @@ -1,65 +0,0 @@ -# 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 -import re - -def execute(): - # NOTE: sequence is important - renamed_fields = get_all_renamed_fields() - - 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 "" - - for name, script, ref_dt in frappe.db.sql("select name, {0} as script, {1} as ref_dt from `tab{2}` where ({3}) {4}".format(script_field, ref_dt_field, dt, cond1, cond2)): - update_script(dt, name, ref_dt, script_field, script, renamed_fields) - -def get_all_renamed_fields(): - from erpnext.patches.v5_0.rename_table_fieldnames import rename_map - - renamed_fields = ( - ("base_amount", "base_net_amount"), - ("net_total", "base_net_total"), - ("net_total_export", "total"), - ("net_total_import", "total"), - ("other_charges_total", "base_total_taxes_and_charges"), - ("other_charges_total_export", "total_taxes_and_charges"), - ("other_charges_added", "base_taxes_and_charges_added"), - ("other_charges_added_import", "taxes_and_charges_added"), - ("other_charges_deducted", "base_taxes_and_charges_deducted"), - ("other_charges_deducted_import", "taxes_and_charges_deducted"), - ("total_tax", "base_total_taxes_and_charges"), - ("grand_total", "base_grand_total"), - ("grand_total_export", "grand_total"), - ("grand_total_import", "grand_total"), - ("rounded_total", "base_rounded_total"), - ("rounded_total_export", "rounded_total"), - ("rounded_total_import", "rounded_total"), - ("in_words", "base_in_words"), - ("in_words_export", "in_words"), - ("in_words_import", "in_words"), - ("tax_amount", "base_tax_amount"), - ("tax_amount_after_discount_amount", "base_tax_amount_after_discount_amount"), - ) - - for fields in rename_map.values(): - renamed_fields += tuple(fields) - - return renamed_fields - -def update_script(dt, name, ref_dt, script_field, script, renamed_fields): - for from_field, to_field in renamed_fields: - if from_field != "entries": - script = re.sub(r"\b{}\b".format(from_field), to_field, script) - - if ref_dt == "Journal Entry": - script = re.sub(r"\bentries\b", "accounts", script) - elif ref_dt == "Bank Reconciliation": - script = re.sub(r"\bentries\b", "journal_entries", script) - elif ref_dt in ("Sales Invoice", "Purchase Invoice"): - script = re.sub(r"\bentries\b", "items", script) - - frappe.db.set_value(dt, name, script_field, script) \ No newline at end of file diff --git a/erpnext/patches/v5_0/repost_gle_for_jv_with_multiple_party.py b/erpnext/patches/v5_0/repost_gle_for_jv_with_multiple_party.py deleted file mode 100644 index 76efdcc7c6..0000000000 --- a/erpnext/patches/v5_0/repost_gle_for_jv_with_multiple_party.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe - -def execute(): - je_list = frappe.db.sql_list(""" - select par.name from `tabJournal Entry` par - where par.docstatus=1 and par.creation > '2015-03-01' - and (select count(distinct child.party) from `tabJournal Entry Account` child - where par.name=child.parent and ifnull(child.party, '') != '') > 1 - """) - - for d in je_list: - # delete existing gle - frappe.db.sql("delete from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s", d) - - # repost gl entries - je = frappe.get_doc("Journal Entry", d) - je.make_gl_entries() - - if je_list: - print(je_list) - - \ No newline at end of file diff --git a/erpnext/patches/v5_0/repost_requested_qty.py b/erpnext/patches/v5_0/repost_requested_qty.py deleted file mode 100644 index 6af71f3fc4..0000000000 --- a/erpnext/patches/v5_0/repost_requested_qty.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty - - count=0 - for item_code, warehouse in frappe.db.sql("""select distinct item_code, warehouse - from `tabMaterial Request Item` where docstatus = 1"""): - try: - count += 1 - update_bin_qty(item_code, warehouse, { - "indented_qty": get_indented_qty(item_code, warehouse), - }) - if count % 200 == 0: - frappe.db.commit() - except: - frappe.db.rollback() diff --git a/erpnext/patches/v5_0/reset_values_in_tools.py b/erpnext/patches/v5_0/reset_values_in_tools.py deleted file mode 100644 index fd970ba1b0..0000000000 --- a/erpnext/patches/v5_0/reset_values_in_tools.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - for dt in ["Payment Tool", "Bank Reconciliation", "Payment Reconciliation", "Leave Control Panel", - "Salary Manager", "Upload Attenadance", "Production Planning Tool", "BOM Update Tool", "Customize Form", - "Employee Attendance Tool", "Rename Tool", "BOM Update Tool", "Process Payroll", "Naming Series"]: - frappe.db.sql("delete from `tabSingles` where doctype=%s", dt) - \ No newline at end of file diff --git a/erpnext/patches/v5_0/set_appraisal_remarks.py b/erpnext/patches/v5_0/set_appraisal_remarks.py deleted file mode 100644 index 8652c32cf0..0000000000 --- a/erpnext/patches/v5_0/set_appraisal_remarks.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Appraisal") - frappe.db.sql("update `tabAppraisal` set remarks = comments") \ No newline at end of file diff --git a/erpnext/patches/v5_0/set_default_company_in_bom.py b/erpnext/patches/v5_0/set_default_company_in_bom.py deleted file mode 100644 index a5cd761119..0000000000 --- a/erpnext/patches/v5_0/set_default_company_in_bom.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doc("manufacturing", "doctype", "bom") - company = frappe.db.get_value("Global Defaults", None, "default_company") - frappe.db.sql("""update `tabBOM` set company = %s""",company) diff --git a/erpnext/patches/v5_0/set_footer_address.py b/erpnext/patches/v5_0/set_footer_address.py deleted file mode 100644 index 8120d834e1..0000000000 --- a/erpnext/patches/v5_0/set_footer_address.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("System Settings") - ss = frappe.get_doc("System Settings", "System Settings") - ss.email_footer_address = frappe.db.get_default("company") - ss.flags.ignore_mandatory = True - ss.save() diff --git a/erpnext/patches/v5_0/stock_entry_update_value.py b/erpnext/patches/v5_0/stock_entry_update_value.py deleted file mode 100644 index ba1af310f5..0000000000 --- a/erpnext/patches/v5_0/stock_entry_update_value.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - for d in frappe.db.get_all("Stock Entry"): - se = frappe.get_doc("Stock Entry", d.name) - se.set_total_incoming_outgoing_value() - se.db_update() diff --git a/erpnext/patches/v5_0/taxes_and_totals_in_party_currency.py b/erpnext/patches/v5_0/taxes_and_totals_in_party_currency.py deleted file mode 100644 index 76d10820b5..0000000000 --- a/erpnext/patches/v5_0/taxes_and_totals_in_party_currency.py +++ /dev/null @@ -1,80 +0,0 @@ -# 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.model.meta import get_field_precision -from frappe.custom.doctype.property_setter.property_setter import make_property_setter - -def execute(): - selling_doctypes = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"] - buying_doctypes = ["Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"] - - for dt in selling_doctypes: - update_values(dt, "Sales Taxes and Charges") - - for dt in buying_doctypes: - update_values(dt, "Purchase Taxes and Charges") - -def update_values(dt, tax_table): - frappe.reload_doctype(dt) - frappe.reload_doctype(dt + " Item") - frappe.reload_doctype(tax_table) - - net_total_precision = get_field_precision(frappe.get_meta(dt).get_field("net_total")) - for field in ("total", "base_total", "base_net_total"): - make_property_setter(dt, field, "precision", net_total_precision, "Select") - - rate_field_precision = get_field_precision(frappe.get_meta(dt + " Item").get_field("rate")) - for field in ("net_rate", "base_net_rate", "net_amount", "base_net_amount", "base_rate", "base_amount"): - make_property_setter(dt + " Item", field, "precision", rate_field_precision, "Select") - - tax_amount_precision = get_field_precision(frappe.get_meta(tax_table).get_field("tax_amount")) - for field in ("base_tax_amount", "total", "base_total", "tax_amount_after_discount_amount", - "base_tax_amount_after_discount_amount"): - make_property_setter(tax_table, field, "precision", tax_amount_precision, "Select") - - # update net_total, discount_on - frappe.db.sql(""" - UPDATE - `tab{0}` - SET - total = round(net_total, {1}), - base_total = round(net_total*conversion_rate, {1}), - net_total = round(base_net_total / conversion_rate, {1}), - apply_discount_on = "Grand Total" - WHERE - docstatus < 2 - """.format(dt, net_total_precision)) - - # update net_amount - frappe.db.sql(""" - UPDATE - `tab{0}` par, `tab{1}` item - SET - item.base_net_amount = round(item.base_amount, {2}), - item.base_net_rate = round(item.base_rate, {2}), - item.net_amount = round(item.base_amount / par.conversion_rate, {2}), - item.net_rate = round(item.base_rate / par.conversion_rate, {2}), - item.base_amount = round(item.amount * par.conversion_rate, {2}), - item.base_rate = round(item.rate * par.conversion_rate, {2}) - WHERE - par.name = item.parent - and par.docstatus < 2 - """.format(dt, dt + " Item", rate_field_precision)) - - # update tax in party currency - frappe.db.sql(""" - UPDATE - `tab{0}` par, `tab{1}` tax - SET - tax.base_tax_amount = round(tax.tax_amount, {2}), - tax.tax_amount = round(tax.tax_amount / par.conversion_rate, {2}), - tax.base_total = round(tax.total, {2}), - tax.total = round(tax.total / conversion_rate, {2}), - tax.base_tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, {2}), - tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount / conversion_rate, {2}) - WHERE - par.name = tax.parent - and par.docstatus < 2 - """.format(dt, tax_table, tax_amount_precision)) diff --git a/erpnext/patches/v5_0/update_account_types.py b/erpnext/patches/v5_0/update_account_types.py deleted file mode 100644 index 424743efaa..0000000000 --- a/erpnext/patches/v5_0/update_account_types.py +++ /dev/null @@ -1,20 +0,0 @@ -# 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 - -def execute(): - for company in frappe.db.get_all("Company"): - company = frappe.get_doc("Company", company.name) - - match_types = ("Stock Received But Not Billed", "Stock Adjustment", "Expenses Included In Valuation", - "Cost of Goods Sold") - - for account_type in match_types: - account_name = "{0} - {1}".format(account_type, company.abbr) - current_account_type = frappe.db.get_value("Account", account_name, "account_type") - if current_account_type != account_type: - frappe.db.set_value("Account", account_name, "account_type", account_type) - - company.set_default_accounts() diff --git a/erpnext/patches/v5_0/update_advance_paid.py b/erpnext/patches/v5_0/update_advance_paid.py deleted file mode 100644 index 74e71e84c8..0000000000 --- a/erpnext/patches/v5_0/update_advance_paid.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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 - -def execute(): - for dt in ("Sales Order", "Purchase Order"): - orders_with_advance = frappe.db.sql("""select name from `tab{0}` - where docstatus < 2 and ifnull(advance_paid, 0) != 0""".format(dt), as_dict=1) - - for order in orders_with_advance: - frappe.get_doc(dt, order.name).set_total_advance_paid() \ No newline at end of file diff --git a/erpnext/patches/v5_0/update_companywise_payment_account.py b/erpnext/patches/v5_0/update_companywise_payment_account.py deleted file mode 100644 index fb4b919c85..0000000000 --- a/erpnext/patches/v5_0/update_companywise_payment_account.py +++ /dev/null @@ -1,21 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doc('accounts', 'doctype', 'mode_of_payment') - frappe.reload_doc('accounts', 'doctype', 'mode_of_payment_account') - - mode_of_payment_list = frappe.db.sql("""select name, default_account - from `tabMode of Payment`""", as_dict=1) - - for d in mode_of_payment_list: - if d.get("default_account"): - parent_doc = frappe.get_doc("Mode of Payment", d.get("name")) - - parent_doc.set("accounts", - [{"company": frappe.db.get_value("Account", d.get("default_account"), "company"), - "default_account": d.get("default_account")}]) - parent_doc.save() diff --git a/erpnext/patches/v5_0/update_dn_against_doc_fields.py b/erpnext/patches/v5_0/update_dn_against_doc_fields.py deleted file mode 100644 index 56f4f484b1..0000000000 --- a/erpnext/patches/v5_0/update_dn_against_doc_fields.py +++ /dev/null @@ -1,14 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doc('stock', 'doctype', 'delivery_note_item') - - frappe.db.sql("""update `tabDelivery Note Item` set so_detail = prevdoc_detail_docname - where ifnull(against_sales_order, '') != ''""") - - frappe.db.sql("""update `tabDelivery Note Item` set si_detail = prevdoc_detail_docname - where ifnull(against_sales_invoice, '') != ''""") diff --git a/erpnext/patches/v5_0/update_from_bom.py b/erpnext/patches/v5_0/update_from_bom.py deleted file mode 100644 index 4b3e62d7a5..0000000000 --- a/erpnext/patches/v5_0/update_from_bom.py +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doctype("Stock Entry") - frappe.db.sql("update `tabStock Entry` set from_bom = if(ifnull(bom_no, '')='', 0, 1)") diff --git a/erpnext/patches/v5_0/update_frozen_accounts_permission_role.py b/erpnext/patches/v5_0/update_frozen_accounts_permission_role.py deleted file mode 100644 index b52785ae60..0000000000 --- a/erpnext/patches/v5_0/update_frozen_accounts_permission_role.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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 - -def execute(): - account_settings = frappe.get_doc("Accounts Settings") - - if not account_settings.frozen_accounts_modifier and account_settings.bde_auth_role: - frappe.db.set_value("Accounts Settings", None, - "frozen_accounts_modifier", account_settings.bde_auth_role) - diff --git a/erpnext/patches/v5_0/update_item_and_description_again.py b/erpnext/patches/v5_0/update_item_and_description_again.py deleted file mode 100644 index 35dedcc072..0000000000 --- a/erpnext/patches/v5_0/update_item_and_description_again.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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 import cstr -import re - -def execute(): - item_details = frappe._dict() - for d in frappe.db.sql("select name, description from `tabItem`", as_dict=1): - description = cstr(d.description).strip() - new_desc = extract_description(description) - - item_details.setdefault(d.name, frappe._dict({ - "old_description": description, - "new_description": new_desc - })) - - - dt_list= ["Purchase Order Item","Supplier Quotation Item", "BOM", "BOM Explosion Item" , \ - "BOM Item", "Opportunity Item" , "Quotation Item" , "Sales Order Item" , "Delivery Note Item" , \ - "Material Request Item" , "Purchase Receipt Item" , "Stock Entry Detail"] - for dt in dt_list: - frappe.reload_doctype(dt) - records = frappe.db.sql("""select name, `{0}` as item_code, description from `tab{1}` - where description is not null and description like '%%]*\>".format(tag), "", desc) - - return desc diff --git a/erpnext/patches/v5_0/update_item_desc_in_invoice.py b/erpnext/patches/v5_0/update_item_desc_in_invoice.py deleted file mode 100644 index dba35d5693..0000000000 --- a/erpnext/patches/v5_0/update_item_desc_in_invoice.py +++ /dev/null @@ -1,52 +0,0 @@ -# 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.website.utils import find_first_image -from frappe.utils import cstr -import re - -def execute(): - item_details = frappe._dict() - for d in frappe.db.sql("select name, description, image from `tabItem`", as_dict=1): - description = cstr(d.description).strip() - item_details.setdefault(d.name, frappe._dict({ - "description": description, - "image": d.image - })) - - - dt_list= ["Sales Invoice Item","Purchase Invoice Item"] - for dt in dt_list: - frappe.reload_doctype(dt) - records = frappe.db.sql("""select name, item_code, description from `tab{0}` - where ifnull(item_code, '') != '' and description is not null """.format(dt), as_dict=1) - - count = 1 - for d in records: - if item_details.get(d.item_code) and cstr(d.description) == item_details.get(d.item_code).description: - desc = item_details.get(d.item_code).description - image = item_details.get(d.item_code).image - else: - desc, image = extract_image_and_description(cstr(d.description)) - - if not image: - item_detail = item_details.get(d.item_code) - if item_detail: - image = item_detail.image - - frappe.db.sql("""update `tab{0}` set description = %s, image = %s - where name = %s """.format(dt), (desc, image, d.name)) - - count += 1 - if count % 500 == 0: - frappe.db.commit() - - -def extract_image_and_description(data): - image_url = find_first_image(data) - desc = data - for tag in ("img", "table", "tr", "td"): - desc = re.sub("\]*\>".format(tag), "", desc) - return desc, image_url \ No newline at end of file diff --git a/erpnext/patches/v5_0/update_item_description_and_image.py b/erpnext/patches/v5_0/update_item_description_and_image.py deleted file mode 100644 index 75df39ee39..0000000000 --- a/erpnext/patches/v5_0/update_item_description_and_image.py +++ /dev/null @@ -1,54 +0,0 @@ -# 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.website.utils import find_first_image -from frappe.utils import cstr -import re - -def execute(): - item_details = frappe._dict() - for d in frappe.db.sql("select name, description_html, description from `tabItem`", as_dict=1): - description = cstr(d.description_html).strip() or cstr(d.description).strip() - image_url, new_desc = extract_image_and_description(description) - - item_details.setdefault(d.name, frappe._dict({ - "old_description": description, - "new_description": new_desc, - "image_url": image_url - })) - - - dt_list= ["Purchase Order Item","Supplier Quotation Item", "BOM", "BOM Explosion Item" , \ - "BOM Item", "Opportunity Item" , "Quotation Item" , "Sales Order Item" , "Delivery Note Item" , \ - "Material Request Item" , "Purchase Receipt Item" , "Stock Entry Detail"] - for dt in dt_list: - frappe.reload_doctype(dt) - records = frappe.db.sql("""select name, `{0}` as item_code, description from `tab{1}` - where description is not null and image is null and description like '%%]+\>", "", data) - - return image_url, desc diff --git a/erpnext/patches/v5_0/update_item_name_in_bom.py b/erpnext/patches/v5_0/update_item_name_in_bom.py deleted file mode 100644 index 5781542a2a..0000000000 --- a/erpnext/patches/v5_0/update_item_name_in_bom.py +++ /dev/null @@ -1,18 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doc("manufacturing", "doctype", "bom") - frappe.reload_doc("manufacturing", "doctype", "bom_item") - frappe.reload_doc("manufacturing", "doctype", "bom_explosion_item") - frappe.reload_doc("manufacturing", "doctype", "bom_operation") - - frappe.db.sql("""update `tabBOM` as bom set bom.item_name = \ - ( select item.item_name from `tabItem` as item where item.name = bom.item)""") - frappe.db.sql("""update `tabBOM Item` as bomItem set bomItem.item_name = ( select item.item_name \ - from `tabItem` as item where item.name = bomItem.item_code)""") - frappe.db.sql("""update `tabBOM Explosion Item` as explosionItem set explosionItem.item_name = \ - ( select item.item_name from `tabItem` as item where item.name = explosionItem.item_code)""") diff --git a/erpnext/patches/v5_0/update_journal_entry_title.py b/erpnext/patches/v5_0/update_journal_entry_title.py deleted file mode 100644 index eaa2be054f..0000000000 --- a/erpnext/patches/v5_0/update_journal_entry_title.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Journal Entry") - frappe.db.sql("""update `tabJournal Entry` set title = - if(ifnull(pay_to_recd_from, "")!="", pay_to_recd_from, - (select account from `tabJournal Entry Account` - where parent=`tabJournal Entry`.name and idx=1 limit 1))""") diff --git a/erpnext/patches/v5_0/update_material_transfer_for_manufacture.py b/erpnext/patches/v5_0/update_material_transfer_for_manufacture.py deleted file mode 100644 index f31c9fed4d..0000000000 --- a/erpnext/patches/v5_0/update_material_transfer_for_manufacture.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.db.sql("""update `tabStock Entry` set purpose='Material Transfer for Manufacture' - where ifnull(work_order, '')!='' and purpose='Material Transfer'""") diff --git a/erpnext/patches/v5_0/update_material_transferred_for_manufacturing.py b/erpnext/patches/v5_0/update_material_transferred_for_manufacturing.py deleted file mode 100644 index 2a09aa29af..0000000000 --- a/erpnext/patches/v5_0/update_material_transferred_for_manufacturing.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Work Order") - frappe.db.sql("""update `tabWork Order` set material_transferred_for_manufacturing= - (select sum(fg_completed_qty) from `tabStock Entry` - where docstatus=1 - and work_order=`tabWork Order`.name - and purpose = "Material Transfer for Manufacture")""") diff --git a/erpnext/patches/v5_0/update_material_transferred_for_manufacturing_again.py b/erpnext/patches/v5_0/update_material_transferred_for_manufacturing_again.py deleted file mode 100644 index 5847c83d38..0000000000 --- a/erpnext/patches/v5_0/update_material_transferred_for_manufacturing_again.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - wo_order_qty_transferred = frappe._dict() - for se in frappe.db.sql("""select work_order, sum(fg_completed_qty) as transferred_qty - from `tabStock Entry` - where docstatus=1 and ifnull(work_order, '') != '' - and purpose = 'Material Transfer for Manufacture' - group by work_order""", as_dict=1): - wo_order_qty_transferred.setdefault(se.work_order, se.transferred_qty) - - for d in frappe.get_all("Work Order", filters={"docstatus": 1}, fields=["name", "qty"]): - if d.name in wo_order_qty_transferred: - material_transferred_for_manufacturing = wo_order_qty_transferred.get(d.name) \ - if wo_order_qty_transferred.get(d.name) <= d.qty else d.qty - - frappe.db.sql("""update `tabWork Order` set material_transferred_for_manufacturing=%s - where name=%s""", (material_transferred_for_manufacturing, d.name)) \ No newline at end of file diff --git a/erpnext/patches/v5_0/update_operation_description.py b/erpnext/patches/v5_0/update_operation_description.py deleted file mode 100644 index 4ce32f35f1..0000000000 --- a/erpnext/patches/v5_0/update_operation_description.py +++ /dev/null @@ -1,11 +0,0 @@ -# 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 -import frappe.permissions - -def execute(): - if "opn_description" in frappe.db.get_table_columns("BOM Operation"): - frappe.db.sql("""update `tabBOM Operation` set description = opn_description - where ifnull(description, '') = ''""") \ No newline at end of file diff --git a/erpnext/patches/v5_0/update_opportunity.py b/erpnext/patches/v5_0/update_opportunity.py deleted file mode 100644 index 8eb45c48e7..0000000000 --- a/erpnext/patches/v5_0/update_opportunity.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('crm', 'doctype', 'opportunity') - frappe.reload_doc('crm', 'doctype', 'opportunity_item') - - # all existing opportunities were with items - frappe.db.sql("update `tabDocType` set module = 'CRM' where name='Opportunity Item'") - frappe.db.sql("update tabOpportunity set with_items=1, title=customer_name") - frappe.db.sql("update `tabEmail Account` set append_to='Opportunity' where append_to='Lead'") diff --git a/erpnext/patches/v5_0/update_projects.py b/erpnext/patches/v5_0/update_projects.py deleted file mode 100644 index 68e03c9bdb..0000000000 --- a/erpnext/patches/v5_0/update_projects.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals -import frappe - -def execute(): - # convert milestones to tasks - frappe.reload_doctype("Project") - frappe.reload_doc("projects", "doctype", "project_task") - frappe.reload_doctype("Task") - frappe.reload_doc("projects", "doctype", "task_depends_on") - frappe.reload_doc("projects", "doctype", "time_log") - - for m in frappe.get_all("Project Milestone", "*"): - if (m.milestone and m.milestone_date - and frappe.db.exists("Project", m.parent)): - subject = (m.milestone[:139] + "…") if (len(m.milestone) > 140) else m.milestone - description = m.milestone - task = frappe.get_doc({ - "doctype": "Task", - "subject": subject, - "description": description if description!=subject else None, - "expected_start_date": m.milestone_date, - "status": "Open" if m.status=="Pending" else "Closed", - "project": m.parent, - }) - task.flags.ignore_mandatory = True - task.insert(ignore_permissions=True) - - # remove project milestone - frappe.delete_doc("DocType", "Project Milestone") - - # remove calendar events for milestone - for e in frappe.get_all("Event", ["name"], {"ref_type": "Project"}): - frappe.delete_doc("Event", e.name) diff --git a/erpnext/patches/v5_0/update_sms_sender.py b/erpnext/patches/v5_0/update_sms_sender.py deleted file mode 100644 index 7ffc703c43..0000000000 --- a/erpnext/patches/v5_0/update_sms_sender.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.db.set_value("SMS Settings", "SMS Settings", "sms_sender_name", - frappe.db.get_single_value("Global Defaults", "sms_sender_name")) diff --git a/erpnext/patches/v5_0/update_tax_amount_after_discount_in_purchase_cycle.py b/erpnext/patches/v5_0/update_tax_amount_after_discount_in_purchase_cycle.py deleted file mode 100644 index 53df9422b3..0000000000 --- a/erpnext/patches/v5_0/update_tax_amount_after_discount_in_purchase_cycle.py +++ /dev/null @@ -1,17 +0,0 @@ -# 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 - -def execute(): - frappe.db.sql(""" - update - `tabPurchase Taxes and Charges` - set - tax_amount_after_discount_amount = tax_amount, - base_tax_amount_after_discount_amount = base_tax_amount - where - ifnull(tax_amount_after_discount_amount, 0) = 0 - and ifnull(base_tax_amount_after_discount_amount, 0) = 0 - """) \ No newline at end of file diff --git a/erpnext/patches/v5_0/update_temporary_account.py b/erpnext/patches/v5_0/update_temporary_account.py deleted file mode 100644 index 078c8714ff..0000000000 --- a/erpnext/patches/v5_0/update_temporary_account.py +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - -def execute(): - frappe.db.sql("""Update `tabAccount` set account_type = 'Temporary' - where account_name in ('Temporary Assets', 'Temporary Liabilities', 'Temporary Opening')""") \ No newline at end of file diff --git a/erpnext/patches/v5_0/update_time_log_title.py b/erpnext/patches/v5_0/update_time_log_title.py deleted file mode 100644 index 8263be0007..0000000000 --- a/erpnext/patches/v5_0/update_time_log_title.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Time Log") - for d in frappe.get_all("Time Log"): - time_log = frappe.get_doc("Time Log", d.name) - time_log.set_title() - frappe.db.set_value("Time Log", time_log.name, "title", time_log.title) diff --git a/erpnext/patches/v5_1/__init__.py b/erpnext/patches/v5_1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v5_1/default_bom.py b/erpnext/patches/v5_1/default_bom.py deleted file mode 100644 index 6484edd603..0000000000 --- a/erpnext/patches/v5_1/default_bom.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import unicode_literals - -import frappe - -def execute(): - frappe.db.sql("""Update `tabItem` as item set default_bom = NULL where - not exists(select name from `tabBOM` as bom where item.default_bom = bom.name and bom.docstatus =1 )""") \ No newline at end of file diff --git a/erpnext/patches/v5_1/fix_against_account.py b/erpnext/patches/v5_1/fix_against_account.py deleted file mode 100644 index a62c15b7d1..0000000000 --- a/erpnext/patches/v5_1/fix_against_account.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import unicode_literals - -import frappe - -from erpnext.accounts.doctype.gl_entry.gl_entry import update_against_account - -def execute(): - from_date = "2015-05-01" - - for doc in frappe.get_all("Journal Entry", - filters={"creation": (">", from_date), "docstatus": "1"}): - - # update in gl_entry - update_against_account("Journal Entry", doc.name) - - # update in jv - doc = frappe.get_doc("Journal Entry", doc.name) - doc.set_against_account() - doc.db_update() - - for doc in frappe.get_all("Sales Invoice", - filters={"creation": (">", from_date), "docstatus": "1"}, - fields=["name", "customer"]): - - frappe.db.sql("""update `tabGL Entry` set against=%s - where voucher_type='Sales Invoice' and voucher_no=%s - and credit > 0 and ifnull(party, '')=''""", - (doc.customer, doc.name)) - - for doc in frappe.get_all("Purchase Invoice", - filters={"creation": (">", from_date), "docstatus": "1"}, - fields=["name", "supplier"]): - - frappe.db.sql("""update `tabGL Entry` set against=%s - where voucher_type='Purchase Invoice' and voucher_no=%s - and debit > 0 and ifnull(party, '')=''""", - (doc.supplier, doc.name)) diff --git a/erpnext/patches/v5_1/rename_roles.py b/erpnext/patches/v5_1/rename_roles.py deleted file mode 100644 index e19c22a614..0000000000 --- a/erpnext/patches/v5_1/rename_roles.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if not frappe.db.exists("Role", "Stock User"): - frappe.rename_doc("Role", "Material User", "Stock User") - if not frappe.db.exists("Role", "Stock Manager"): - frappe.rename_doc("Role", "Material Manager", "Stock Manager") - if not frappe.db.exists("Role", "Item Manager"): - frappe.rename_doc("Role", "Material Master Manager", "Item Manager") diff --git a/erpnext/patches/v5_1/sales_bom_rename.py b/erpnext/patches/v5_1/sales_bom_rename.py deleted file mode 100644 index e06012f3e4..0000000000 --- a/erpnext/patches/v5_1/sales_bom_rename.py +++ /dev/null @@ -1,12 +0,0 @@ -# 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 - -def execute(): - tables = frappe.db.sql_list("show tables") - for old_dt, new_dt in [["Sales BOM Item", "Product Bundle Item"], - ["Sales BOM", "Product Bundle"]]: - if "tab"+new_dt not in tables: - frappe.rename_doc("DocType", old_dt, new_dt, force=True) diff --git a/erpnext/patches/v5_2/__init__.py b/erpnext/patches/v5_2/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v5_2/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v5_2/change_item_selects_to_checks.py b/erpnext/patches/v5_2/change_item_selects_to_checks.py deleted file mode 100644 index 1ee8f6caa5..0000000000 --- a/erpnext/patches/v5_2/change_item_selects_to_checks.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import unicode_literals - -import frappe - -def execute(): - fields = ("is_stock_item", "is_asset_item", "has_batch_no", "has_serial_no", - "is_sales_item", "is_purchase_item", "inspection_required", "is_sub_contracted_item") - - # convert to 1 or 0 - update_str = ", ".join(["`{0}`=if(`{0}`='Yes',1,0)".format(f) for f in fields]) - frappe.db.sql("update tabItem set {0}".format(update_str)) - - frappe.db.commit() - - # alter fields to int - for f in fields: - frappe.db.sql("alter table tabItem change {0} {0} int(1) default '0'".format(f, f)) - - frappe.reload_doctype("Item") diff --git a/erpnext/patches/v5_4/__init__.py b/erpnext/patches/v5_4/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v5_4/cleanup_journal_entry.py b/erpnext/patches/v5_4/cleanup_journal_entry.py deleted file mode 100644 index 6860e6ad09..0000000000 --- a/erpnext/patches/v5_4/cleanup_journal_entry.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import unicode_literals -import frappe -from pymysql import InternalError - -def execute(): - frappe.reload_doctype("Journal Entry Account") - for doctype, fieldname in ( - ("Sales Order", "against_sales_order"), - ("Purchase Order", "against_purchase_order"), - ("Sales Invoice", "against_invoice"), - ("Purchase Invoice", "against_voucher"), - ("Journal Entry", "against_jv"), - ("Expense Claim", "against_expense_claim"), - ): - try: - frappe.db.sql("""update `tabJournal Entry Account` - set reference_type=%s, reference_name={0} where ifnull({0}, '') != '' - """.format(fieldname), doctype) - except InternalError: - # column not found - pass diff --git a/erpnext/patches/v5_4/fix_invoice_outstanding.py b/erpnext/patches/v5_4/fix_invoice_outstanding.py deleted file mode 100644 index 54a1da69ef..0000000000 --- a/erpnext/patches/v5_4/fix_invoice_outstanding.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt - -def execute(): - frappe.reload_doctype("Sales Invoice") - return_entries = frappe.get_list("Sales Invoice", filters={"is_return": 1, "docstatus": 1}, - fields=["debit_to", "customer", "return_against"]) - for d in return_entries: - update_outstanding_amt(d.debit_to, "Customer", d.customer, "Sales Invoice", d.return_against) diff --git a/erpnext/patches/v5_4/fix_missing_item_images.py b/erpnext/patches/v5_4/fix_missing_item_images.py deleted file mode 100644 index c6fe57896f..0000000000 --- a/erpnext/patches/v5_4/fix_missing_item_images.py +++ /dev/null @@ -1,126 +0,0 @@ -from __future__ import print_function, unicode_literals -import frappe -import os -from frappe.utils import get_files_path -from frappe.core.doctype.file.file import get_content_hash - -def execute(): - files_path = get_files_path() - - # get files that don't have attached_to_name but exist - unlinked_files = get_unlinked_files(files_path) - if not unlinked_files: - return - - fixed_files = fix_files_for_item(files_path, unlinked_files) - - # fix remaining files - for key, file_data in unlinked_files.items(): - if key not in fixed_files: - rename_and_set_content_hash(files_path, unlinked_files, key) - frappe.db.commit() - -def fix_files_for_item(files_path, unlinked_files): - fixed_files = [] - - # make a list of files/something and /files/something to check in child table's image column - file_urls = [key for key in unlinked_files.keys()] + ["/" + key for key in unlinked_files.keys()] - file_item_code = get_file_item_code(file_urls) - - for (file_url, item_code), children in file_item_code.items(): - new_file_url = "/files/{0}".format(unlinked_files[file_url]["file_name"]) - - for row in children: - # print file_url, new_file_url, item_code, row.doctype, row.name - - # replace image in these rows with the new file url - frappe.db.set_value(row.doctype, row.name, "image", new_file_url, update_modified=False) - - # set it as attachment of this item code - file_data = frappe.get_doc("File", unlinked_files[file_url]["file"]) - file_data.attached_to_doctype = "Item" - file_data.attached_to_name = item_code - file_data.flags.ignore_folder_validate = True - - try: - file_data.save() - except IOError: - print("File {0} does not exist".format(new_file_url)) - - # marking fix to prevent further errors - fixed_files.append(file_url) - - continue - - # set it as image in Item - if not frappe.db.get_value("Item", item_code, "image"): - frappe.db.set_value("Item", item_code, "image", new_file_url, update_modified=False) - - rename_and_set_content_hash(files_path, unlinked_files, file_url) - - fixed_files.append(file_url) - - # commit - frappe.db.commit() - - return fixed_files - -def rename_and_set_content_hash(files_path, unlinked_files, file_url): - # rename this file - old_filename = os.path.join(files_path, unlinked_files[file_url]["file"]) - new_filename = os.path.join(files_path, unlinked_files[file_url]["file_name"]) - - if not os.path.exists(new_filename): - os.rename(old_filename, new_filename) - - # set content hash if missing - file_data_name = unlinked_files[file_url]["file"] - if not frappe.db.get_value("File", file_data_name, "content_hash"): - with open(new_filename, "r") as f: - content_hash = get_content_hash(f.read()) - frappe.db.set_value("File", file_data_name, "content_hash", content_hash) - -def get_unlinked_files(files_path): - # find files that have the same name as a File doc - # and the file_name mentioned in that File doc doesn't exist - # and it isn't already attached to a doc - unlinked_files = {} - files = os.listdir(files_path) - for file in files: - if not frappe.db.exists("File", {"file_name": file}): - file_data = frappe.db.get_value("File", {"name": file}, - ["file_name", "attached_to_doctype", "attached_to_name"], as_dict=True) - - if (file_data - and file_data.file_name - and file_data.file_name not in files - and not file_data.attached_to_doctype - and not file_data.attached_to_name): - - file_data["file"] = file - unlinked_files["files/{0}".format(file)] = file_data - - return unlinked_files - -def get_file_item_code(file_urls): - # get a map of file_url, item_code and list of documents where file_url will need to be changed in image field - file_item_code = {} - - doctypes = frappe.db.sql_list("""select name from `tabDocType` dt - where istable=1 - and exists (select name from `tabDocField` df where df.parent=dt.name and df.fieldname='item_code') - and exists (select name from `tabDocField` df where df.parent=dt.name and df.fieldname='image')""") - - for doctype in doctypes: - result = frappe.db.sql("""select name, image, item_code, '{0}' as doctype from `tab{0}` - where image in ({1})""".format(doctype, ", ".join(["%s"]*len(file_urls))), - file_urls, as_dict=True) - - for r in result: - key = (r.image, r.item_code) - if key not in file_item_code: - file_item_code[key] = [] - - file_item_code[key].append(r) - - return file_item_code diff --git a/erpnext/patches/v5_4/fix_reserved_qty_and_sle_for_packed_items.py b/erpnext/patches/v5_4/fix_reserved_qty_and_sle_for_packed_items.py deleted file mode 100644 index 6eb3994c7c..0000000000 --- a/erpnext/patches/v5_4/fix_reserved_qty_and_sle_for_packed_items.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from erpnext.stock.stock_balance import repost_actual_qty - -def execute(): - cancelled_invoices = frappe.db.sql_list("""select name from `tabSales Invoice` - where docstatus = 2 and ifnull(update_stock, 0) = 1""") - - if cancelled_invoices: - repost_for = frappe.db.sql("""select distinct item_code, warehouse from `tabStock Ledger Entry` - where voucher_type = 'Sales Invoice' and voucher_no in (%s)""" - % (', '.join(['%s']*len(cancelled_invoices))), tuple(cancelled_invoices)) - - frappe.db.sql("""delete from `tabStock Ledger Entry` - where voucher_type = 'Sales Invoice' and voucher_no in (%s)""" - % (', '.join(['%s']*len(cancelled_invoices))), tuple(cancelled_invoices)) - - for item_code, warehouse in repost_for: - repost_actual_qty(item_code, warehouse) \ No newline at end of file diff --git a/erpnext/patches/v5_4/notify_system_managers_regarding_wrong_tax_calculation.py b/erpnext/patches/v5_4/notify_system_managers_regarding_wrong_tax_calculation.py deleted file mode 100644 index ba311225bb..0000000000 --- a/erpnext/patches/v5_4/notify_system_managers_regarding_wrong_tax_calculation.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe -from frappe.email import sendmail_to_system_managers -from frappe.utils import get_link_to_form - -def execute(): - wrong_records = [] - for dt in ("Quotation", "Sales Order", "Delivery Note", "Sales Invoice", - "Purchase Order", "Purchase Receipt", "Purchase Invoice"): - records = frappe.db.sql_list("""select name from `tab{0}` - where apply_discount_on = 'Net Total' and ifnull(discount_amount, 0) != 0 - and modified >= '2015-02-17' and docstatus=1""".format(dt)) - - if records: - records = [get_link_to_form(dt, d) for d in records] - wrong_records.append([dt, records]) - - if wrong_records: - content = """Dear System Manager, - -Due to an error related to Discount Amount on Net Total, tax calculation might be wrong in the following records. We did not fix the tax amount automatically because it can corrupt the entries, so we request you to check these records and amend if you found the calculation wrong. - -Please check following Entries: - -%s - - -Regards, - -Administrator""" % "\n".join([(d[0] + ": " + ", ".join(d[1])) for d in wrong_records]) - try: - sendmail_to_system_managers("[Important] [ERPNext] Tax calculation might be wrong, please check.", content) - except: - pass - - print("="*50) - print(content) - print("="*50) \ No newline at end of file diff --git a/erpnext/patches/v5_4/set_root_and_report_type.py b/erpnext/patches/v5_4/set_root_and_report_type.py deleted file mode 100644 index 9147644da2..0000000000 --- a/erpnext/patches/v5_4/set_root_and_report_type.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - roots = frappe.db.sql("""select lft, rgt, report_type, root_type - from `tabAccount` where ifnull(parent_account, '')=''""", as_dict=1) - for d in roots: - frappe.db.sql("update `tabAccount` set report_type=%s, root_type=%s where lft > %s and rgt < %s", - (d.report_type, d.root_type, d.lft, d.rgt)) \ No newline at end of file diff --git a/erpnext/patches/v5_4/stock_entry_additional_costs.py b/erpnext/patches/v5_4/stock_entry_additional_costs.py deleted file mode 100644 index 3a98deb918..0000000000 --- a/erpnext/patches/v5_4/stock_entry_additional_costs.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from frappe.utils import flt - -def execute(): - frappe.reload_doctype("Stock Entry") - frappe.reload_doctype("Stock Entry Detail") - frappe.reload_doctype("Landed Cost Taxes and Charges") - - stock_entry_db_columns = frappe.db.get_table_columns("Stock Entry") - if "additional_operating_cost" in stock_entry_db_columns: - operating_cost_fieldname = "additional_operating_cost" - elif "total_fixed_cost" in stock_entry_db_columns: - operating_cost_fieldname = "total_fixed_cost" - else: - return - - - frappe.db.sql("""update `tabStock Entry Detail` sed, `tabStock Entry` se - set sed.valuation_rate=sed.incoming_rate, sed.basic_rate=sed.incoming_rate, sed.basic_amount=sed.amount - where sed.parent = se.name - and (se.purpose not in ('Manufacture', 'Repack') or ifnull({0}, 0)=0) - """.format(operating_cost_fieldname)) - - - stock_entries = frappe.db.sql_list("""select name from `tabStock Entry` - where purpose in ('Manufacture', 'Repack') and ifnull({0}, 0)!=0 - and docstatus < 2""".format(operating_cost_fieldname)) - - for d in stock_entries: - stock_entry = frappe.get_doc("Stock Entry", d) - stock_entry.append("additional_costs", { - "description": "Additional Operating Cost", - "amount": stock_entry.get(operating_cost_fieldname) - }) - - number_of_fg_items = len([t.t_warehouse for t in stock_entry.get("items") if t.t_warehouse]) - - for d in stock_entry.get("items"): - d.valuation_rate = d.incoming_rate - - if d.bom_no or (d.t_warehouse and number_of_fg_items == 1): - d.additional_cost = stock_entry.get(operating_cost_fieldname) - - d.basic_rate = flt(d.valuation_rate) - flt(d.additional_cost) - d.basic_amount = flt(flt(d.basic_rate) *flt(d.transfer_qty), d.precision("basic_amount")) - - stock_entry.flags.ignore_validate = True - stock_entry.flags.ignore_validate_update_after_submit = True - stock_entry.save() diff --git a/erpnext/patches/v5_4/update_purchase_cost_against_project.py b/erpnext/patches/v5_4/update_purchase_cost_against_project.py deleted file mode 100644 index 1b917c83c4..0000000000 --- a/erpnext/patches/v5_4/update_purchase_cost_against_project.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - for p in frappe.get_all("Project"): - purchase_cost = frappe.db.sql("""select sum(ifnull(base_net_amount, 0)) - from `tabPurchase Invoice Item` where project = %s and docstatus=1""", p.name) - purchase_cost = purchase_cost and purchase_cost[0][0] or 0 - - frappe.db.set_value("Project", p.name, "total_purchase_cost", purchase_cost) \ No newline at end of file diff --git a/erpnext/patches/v5_7/__init__.py b/erpnext/patches/v5_7/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v5_7/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v5_7/item_template_attributes.py b/erpnext/patches/v5_7/item_template_attributes.py deleted file mode 100644 index 6aa81f79b2..0000000000 --- a/erpnext/patches/v5_7/item_template_attributes.py +++ /dev/null @@ -1,124 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe -from frappe.exceptions import SQLError - -def execute(): - """ - Structure History: - 1. Item and Item Variant - 2. Item, Variant Attribute, Manage Variants and Manage Variant Items - 3. Item, Item Variant Attribute, Item Attribute and Item Attribute Type (latest) - """ - rename_and_reload_doctypes() - - variant_templates = frappe.get_all("Item", filters={"has_variants": 1}, limit_page_length=1) - if not variant_templates: - # database does not have items that have variants - # so no point in running the patch - return - - variant_attributes = frappe.get_all("Item Variant Attribute", fields=["*"], limit_page_length=1) - - if variant_attributes: - # manage variant patch is already applied - migrate_manage_variants() - - else: - # old structure based on "Item Variant" table - try: - migrate_item_variants() - - except SQLError: - print("`tabItem Variant` not found") - -def rename_and_reload_doctypes(): - if "tabVariant Attribute" in frappe.db.get_tables(): - frappe.rename_doc("DocType", "Variant Attribute", "Item Variant Attribute") - - frappe.reload_doctype("Item") - frappe.reload_doc("Stock", "DocType", "Item Variant Attribute") - frappe.reload_doc("Stock", "DocType", "Item Attribute Value") - frappe.reload_doc("Stock", "DocType", "Item Attribute") - -def migrate_manage_variants(): - item_attribute = {} - for d in frappe.db.sql("""select DISTINCT va.attribute, i.variant_of - from `tabItem Variant Attribute` va, `tabItem` i - where va.parent = i.name and ifnull(i.variant_of, '')!=''""", as_dict=1): - item_attribute.setdefault(d.variant_of, []).append({"attribute": d.attribute}) - - for item, attributes in item_attribute.items(): - template = frappe.get_doc("Item", item) - template.set('attributes', attributes) - template.save() - -# patch old style -def migrate_item_variants(): - for item in frappe.get_all("Item", filters={"has_variants": 1}): - all_variants = frappe.get_all("Item", filters={"variant_of": item.name}, fields=["name", "description"]) - item_attributes = frappe.db.sql("""select distinct item_attribute, item_attribute_value - from `tabItem Variant` where parent=%s""", item.name) - - if not item_attributes and not all_variants: - item = frappe.get_doc("Item", item.name) - item.has_variants = 0 - item.save() - continue - - attribute_value_options = {} - for attribute, value in item_attributes: - attribute_value_options.setdefault(attribute, []).append(value) - - possible_combinations = get_possible_combinations(attribute_value_options) - - for variant in all_variants: - for combination in possible_combinations: - match = True - for attribute, value in combination.items(): - if "{0}: {1}".format(attribute, value) not in variant.description: - match = False - break - - if match: - # found the right variant - save_attributes_in_variant(variant, combination) - break - - save_attributes_in_template(item, attribute_value_options) - - frappe.delete_doc("DocType", "Item Variant") - -def save_attributes_in_template(item, attribute_value_options): - # store attribute in Item Variant Attribute table for template - template = frappe.get_doc("Item", item) - template.set("attributes", [{"attribute": attribute} for attribute in attribute_value_options.keys()]) - template.save() - -def get_possible_combinations(attribute_value_options): - possible_combinations = [] - - for attribute, values in attribute_value_options.items(): - if not possible_combinations: - for v in values: - possible_combinations.append({attribute: v}) - - else: - for v in values: - for combination in possible_combinations: - combination[attribute] = v - - return possible_combinations - -def save_attributes_in_variant(variant, combination): - # add data into attributes table - variant_item = frappe.get_doc("Item", variant.name) - variant_item.set("attributes", []) - for attribute, value in combination.items(): - variant_item.append("attributes", { - "attribute": attribute, - "attribute_value": value - }) - variant_item.save() diff --git a/erpnext/patches/v5_8/__init__.py b/erpnext/patches/v5_8/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v5_8/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v5_8/add_credit_note_print_heading.py b/erpnext/patches/v5_8/add_credit_note_print_heading.py deleted file mode 100644 index 476cbc8956..0000000000 --- a/erpnext/patches/v5_8/add_credit_note_print_heading.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ - -def execute(): - for print_heading in (_("Credit Note"), _("Debit Note")): - if not frappe.db.exists("Print Heading", print_heading): - frappe.get_doc({ - "doctype": "Print Heading", - "print_heading": print_heading - }).insert() diff --git a/erpnext/patches/v5_8/tax_rule.py b/erpnext/patches/v5_8/tax_rule.py deleted file mode 100644 index 8da28ba4c9..0000000000 --- a/erpnext/patches/v5_8/tax_rule.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("accounts", "doctype", "tax_rule") - - customers = frappe.db.sql("""select name, default_taxes_and_charges from tabCustomer where - ifnull(default_taxes_and_charges, '') != '' """, as_dict=1) - - for d in customers: - if not frappe.db.sql("select name from `tabTax Rule` where customer=%s", d.name): - tr = frappe.new_doc("Tax Rule") - tr.tax_type = "Sales" - tr.customer = d.name - tr.sales_tax_template = d.default_taxes_and_charges - tr.save() - - - suppliers = frappe.db.sql("""select name, default_taxes_and_charges from tabSupplier where - ifnull(default_taxes_and_charges, '') != '' """, as_dict=1) - - for d in suppliers: - if not frappe.db.sql("select name from `tabTax Rule` where supplier=%s", d.name): - tr = frappe.new_doc("Tax Rule") - tr.tax_type = "Purchase" - tr.supplier = d.name - tr.purchase_tax_template = d.default_taxes_and_charges - tr.save() \ No newline at end of file diff --git a/erpnext/patches/v5_8/update_order_reference_in_return_entries.py b/erpnext/patches/v5_8/update_order_reference_in_return_entries.py deleted file mode 100644 index 503263834c..0000000000 --- a/erpnext/patches/v5_8/update_order_reference_in_return_entries.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Delivery Note") - frappe.reload_doctype("Sales Invoice") - frappe.reload_doctype("Purchase Receipt") - frappe.reload_doctype("Sales Order Item") - frappe.reload_doctype("Purchase Order Item") - frappe.reload_doctype("Purchase Order Item Supplied") - - # sales return - return_entries = list(frappe.db.sql(""" - select dn.name as name, dn_item.name as row_id, dn.return_against, - dn_item.item_code, "Delivery Note" as doctype - from `tabDelivery Note Item` dn_item, `tabDelivery Note` dn - where dn_item.parent=dn.name and dn.is_return=1 and dn.docstatus < 2 - """, as_dict=1)) - - return_entries += list(frappe.db.sql(""" - select si.name as name, si_item.name as row_id, si.return_against, - si_item.item_code, "Sales Invoice" as doctype, update_stock - from `tabSales Invoice Item` si_item, `tabSales Invoice` si - where si_item.parent=si.name and si.is_return=1 and si.docstatus < 2 - """, as_dict=1)) - - for d in return_entries: - ref_field = "against_sales_order" if d.doctype == "Delivery Note" else "sales_order" - order_details = frappe.db.sql(""" - select {ref_field} as sales_order, so_detail, - (select transaction_date from `tabSales Order` where name=item.{ref_field}) as sales_order_date - from `tab{doctype} Item` item - where - parent=%s - and item_code=%s - and ifnull(so_detail, '') !='' - order by sales_order_date DESC limit 1 - """.format(ref_field=ref_field, doctype=d.doctype), (d.return_against, d.item_code), as_dict=1) - - if order_details: - frappe.db.sql(""" - update `tab{doctype} Item` - set {ref_field}=%s, so_detail=%s - where name=%s - """.format(doctype=d.doctype, ref_field=ref_field), - (order_details[0].sales_order, order_details[0].so_detail, d.row_id)) - - if (d.doctype=="Sales Invoice" and d.update_stock) or d.doctype=="Delivery Note": - doc = frappe.get_doc(d.doctype, d.name) - doc.update_reserved_qty() - - if d.doctype=="Sales Invoice": - doc.status_updater = [] - doc.update_status_updater_args() - - doc.update_prevdoc_status() - - #-------------------------- - # purchase return - return_entries = frappe.db.sql(""" - select pr.name as name, pr_item.name as row_id, pr.return_against, pr_item.item_code - from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr - where pr_item.parent=pr.name and pr.is_return=1 and pr.docstatus < 2 - """, as_dict=1) - - for d in return_entries: - order_details = frappe.db.sql(""" - select prevdoc_docname as purchase_order, prevdoc_detail_docname as po_detail, - (select transaction_date from `tabPurchase Order` where name=item.prevdoc_detail_docname) as purchase_order_date - from `tabPurchase Receipt Item` item - where - parent=%s - and item_code=%s - and ifnull(prevdoc_detail_docname, '') !='' - and ifnull(prevdoc_doctype, '') = 'Purchase Order' and ifnull(prevdoc_detail_docname, '') != '' - order by purchase_order_date DESC limit 1 - """, (d.return_against, d.item_code), as_dict=1) - - if order_details: - frappe.db.sql(""" - update `tabPurchase Receipt Item` - set prevdoc_doctype='Purchase Order', prevdoc_docname=%s, prevdoc_detail_docname=%s - where name=%s - """, (order_details[0].purchase_order, order_details[0].po_detail, d.row_id)) - - pr = frappe.get_doc("Purchase Receipt", d.name) - pr.update_ordered_and_reserved_qty() - pr.update_prevdoc_status() - diff --git a/erpnext/patches/v6_0/__init__.py b/erpnext/patches/v6_0/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v6_0/default_activity_rate.py b/erpnext/patches/v6_0/default_activity_rate.py deleted file mode 100644 index cfbfb723bc..0000000000 --- a/erpnext/patches/v6_0/default_activity_rate.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("projects", "doctype", "activity_cost") - - for cost in frappe.db.get_list("Activity Cost", filters = {"employee": ""}, - fields = ("name", "activity_type", "costing_rate", "billing_rate")): - activity_type = frappe.get_doc("Activity Type", cost.activity_type) - activity_type.costing_rate = cost.costing_rate - activity_type.billing_rate = cost.billing_rate - activity_type.save() - - frappe.delete_doc("Activity Cost", cost.name) diff --git a/erpnext/patches/v6_0/fix_outstanding_amount.py b/erpnext/patches/v6_0/fix_outstanding_amount.py deleted file mode 100644 index 0de689074f..0000000000 --- a/erpnext/patches/v6_0/fix_outstanding_amount.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt - -def execute(): - for dt, party_field, account_field in (("Sales Invoice", "customer", "debit_to"), - ("Purchase Invoice", "supplier", "credit_to")): - - wrong_invoices = frappe.db.sql("""select name, {0} as account from `tab{1}` - where docstatus=1 and ifnull({2}, '')=''""".format(account_field, dt, party_field)) - - for invoice, account in wrong_invoices: - update_outstanding_amt(account, party_field.title(), None, dt, invoice) \ No newline at end of file diff --git a/erpnext/patches/v6_0/fix_planned_qty.py b/erpnext/patches/v6_0/fix_planned_qty.py deleted file mode 100644 index cf7b429249..0000000000 --- a/erpnext/patches/v6_0/fix_planned_qty.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty - -def execute(): - for item_code, warehouse in frappe.db.sql("""select distinct production_item, fg_warehouse - from `tabWork Order`"""): - if frappe.db.exists("Item", item_code) and frappe.db.exists("Warehouse", warehouse): - update_bin_qty(item_code, warehouse, { - "planned_qty": get_planned_qty(item_code, warehouse) - }) diff --git a/erpnext/patches/v6_0/multi_currency.py b/erpnext/patches/v6_0/multi_currency.py deleted file mode 100644 index b4c37fc253..0000000000 --- a/erpnext/patches/v6_0/multi_currency.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - # Reload doctype - for dt in ("Account", "GL Entry", "Journal Entry", - "Journal Entry Account", "Sales Invoice", "Purchase Invoice", "Customer", "Supplier"): - frappe.reload_doctype(dt) - - company_list = frappe.get_all("Company", fields=["name", "default_currency", "default_receivable_account"]) - for company in company_list: - - # update currency in account and gl entry as per company currency - frappe.db.sql("""update `tabAccount` set account_currency = %s - where ifnull(account_currency, '') = '' and company=%s""", (company.default_currency, company.name)) - - # update newly introduced field's value in sales / purchase invoice - frappe.db.sql(""" - update - `tabSales Invoice` - set - base_paid_amount=paid_amount, - base_write_off_amount=write_off_amount, - party_account_currency=%s - where company=%s - """, (company.default_currency, company.name)) - - frappe.db.sql(""" - update - `tabPurchase Invoice` - set - base_write_off_amount=write_off_amount, - party_account_currency=%s - where company=%s - """, (company.default_currency, company.name)) - - # update exchange rate, debit/credit in account currency in Journal Entry - frappe.db.sql(""" - update `tabJournal Entry Account` jea - set exchange_rate=1, - debit_in_account_currency=debit, - credit_in_account_currency=credit, - account_type=(select account_type from `tabAccount` where name=jea.account) - """) - - frappe.db.sql(""" - update `tabJournal Entry Account` jea, `tabJournal Entry` je - set account_currency=%s - where jea.parent = je.name and je.company=%s - """, (company.default_currency, company.name)) - - # update debit/credit in account currency in GL Entry - frappe.db.sql(""" - update - `tabGL Entry` - set - debit_in_account_currency=debit, - credit_in_account_currency=credit, - account_currency=%s - where - company=%s - """, (company.default_currency, company.name)) diff --git a/erpnext/patches/v6_0/set_default_title.py b/erpnext/patches/v6_0/set_default_title.py deleted file mode 100644 index cceff3f480..0000000000 --- a/erpnext/patches/v6_0/set_default_title.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Quotation") - frappe.db.sql("""update tabQuotation set title = customer_name""") - - frappe.reload_doctype("Sales Order") - frappe.db.sql("""update `tabSales Order` set title = customer_name""") - - frappe.reload_doctype("Delivery Note") - frappe.db.sql("""update `tabDelivery Note` set title = customer_name""") - - frappe.reload_doctype("Material Request") - frappe.db.sql("""update `tabMaterial Request` set title = material_request_type""") - - frappe.reload_doctype("Supplier Quotation") - frappe.db.sql("""update `tabSupplier Quotation` set title = supplier_name""") - - frappe.reload_doctype("Purchase Order") - frappe.db.sql("""update `tabPurchase Order` set title = supplier_name""") - - frappe.reload_doctype("Purchase Receipt") - frappe.db.sql("""update `tabPurchase Receipt` set title = supplier_name""") - - frappe.reload_doctype("Purchase Invoice") - frappe.db.sql("""update `tabPurchase Invoice` set title = supplier_name""") - - frappe.reload_doctype("Stock Entry") - frappe.db.sql("""update `tabStock Entry` set title = purpose""") - - frappe.reload_doctype("Sales Invoice") - frappe.db.sql("""update `tabSales Invoice` set title = customer_name""") - - frappe.reload_doctype("Expense Claim") - frappe.db.sql("""update `tabExpense Claim` set title = employee_name""") diff --git a/erpnext/patches/v6_10/__init__.py b/erpnext/patches/v6_10/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v6_10/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v6_10/email_digest_default_quote.py b/erpnext/patches/v6_10/email_digest_default_quote.py deleted file mode 100644 index 6139f1a88a..0000000000 --- a/erpnext/patches/v6_10/email_digest_default_quote.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Email Digest") - frappe.db.sql("update `tabEmail Digest` set add_quote = 1") diff --git a/erpnext/patches/v6_10/fix_billed_amount_in_drop_ship_po.py b/erpnext/patches/v6_10/fix_billed_amount_in_drop_ship_po.py deleted file mode 100644 index d7f72b5880..0000000000 --- a/erpnext/patches/v6_10/fix_billed_amount_in_drop_ship_po.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.db.sql("""update `tabPurchase Order Item` set billed_amt = 0 - where delivered_by_supplier=1 and docstatus=1""") - - drop_ship_pos = frappe.db.sql("""select distinct parent from `tabPurchase Order Item` - where delivered_by_supplier=1 and docstatus=1""") - - for po in drop_ship_pos: - invoices = frappe.db.sql("""select distinct parent from `tabPurchase Invoice Item` - where purchase_order=%s and docstatus=1""", po[0]) - if invoices: - for inv in invoices: - frappe.get_doc("Purchase Invoice", inv[0]).update_qty(update_modified=False) - else: - frappe.db.sql("""update `tabPurchase Order` set per_billed=0 where name=%s""", po[0]) \ No newline at end of file diff --git a/erpnext/patches/v6_10/fix_delivery_status_of_drop_ship_item.py b/erpnext/patches/v6_10/fix_delivery_status_of_drop_ship_item.py deleted file mode 100644 index 9a53b7f5d3..0000000000 --- a/erpnext/patches/v6_10/fix_delivery_status_of_drop_ship_item.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Sales Order Item") - for so_name in frappe.db.sql("""select distinct parent from `tabSales Order Item` - where delivered_by_supplier=1 and docstatus=1"""): - so = frappe.get_doc("Sales Order", so_name[0]) - so.update_delivery_status() - so.set_status(update=True, update_modified=False) \ No newline at end of file diff --git a/erpnext/patches/v6_10/fix_jv_total_amount.py b/erpnext/patches/v6_10/fix_jv_total_amount.py deleted file mode 100644 index 42cb9e9e15..0000000000 --- a/erpnext/patches/v6_10/fix_jv_total_amount.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import unicode_literals -import frappe - -# patch all for-print field (total amount) in Journal Entry in 2015 -def execute(): - for je in frappe.get_all("Journal Entry", filters={"creation": (">", "2015-01-01")}): - je = frappe.get_doc("Journal Entry", je.name) - original = je.total_amount - - je.set_print_format_fields() - - if je.total_amount != original: - je.db_set("total_amount", je.total_amount, update_modified=False) - je.db_set("total_amount_in_words", je.total_amount_in_words, update_modified=False) diff --git a/erpnext/patches/v6_10/fix_ordered_received_billed.py b/erpnext/patches/v6_10/fix_ordered_received_billed.py deleted file mode 100644 index c81a20ec54..0000000000 --- a/erpnext/patches/v6_10/fix_ordered_received_billed.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - not_null_patch_date = frappe.db.sql("""select date(creation) from `tabPatch Log` where patch='frappe.patches.v6_9.int_float_not_null'""") - if not not_null_patch_date: - return - - not_null_patch_date = not_null_patch_date[0][0] - - for doctype in ("Purchase Invoice", "Sales Invoice", "Purchase Order", "Delivery Note", "Installation Note", "Delivery Note", "Purchase Receipt"): - for name in frappe.db.sql_list("""select name from `tab{doctype}` - where docstatus > 0 and (date(creation) >= %(patch_date)s or date(modified) >= %(patch_date)s)""".format(doctype=doctype), - {"patch_date": not_null_patch_date}): - - doc = frappe.get_doc(doctype, name) - doc.update_qty(update_modified=False) diff --git a/erpnext/patches/v6_12/__init__.py b/erpnext/patches/v6_12/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v6_12/repost_entries_with_target_warehouse.py b/erpnext/patches/v6_12/repost_entries_with_target_warehouse.py deleted file mode 100644 index fb5eab4e05..0000000000 --- a/erpnext/patches/v6_12/repost_entries_with_target_warehouse.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe - -""" -This patch is written to fix Stock Ledger Entries and GL Entries -against Delivery Notes and Sales Invoice where Target Warehouse has been set wrongly -due to User Permissions on Warehouse. - -This cannot be run automatically because we can't take a call that -Target Warehouse has been set purposefully or by mistake. -Thats why we left it to the users to take the call, and manually run the patch. - -This patch has 2 main functions, `check()` and `repost()`. -- Run `check` function, to list out all the Sales Orders, Delivery Notes - and Sales Invoice with Target Warehouse. -- Run `repost` function to remove the Target Warehouse value and repost SLE and GLE again. - -To execute this patch run following commands from frappe-bench directory: -``` - bench --site [your-site-name] execute erpnext.patches.v6_12.repost_entries_with_target_warehouse.check - bench --site [your-site-name] backup - bench --site [your-site-name] execute erpnext.patches.v6_12.repost_entries_with_target_warehouse.repost -``` - -Exception Handling: -While reposting, if you get any exception, it will printed on screen. -Mostly it can be due to negative stock issue. If that is the case, follow these steps - - Ensure that stock is available for those items in the mentioned warehouse on the date mentioned in the error - - Execute `repost` funciton again -""" - -def check(): - so_list = get_affected_sales_order() - dn_list = get_affected_delivery_notes() - si_list = get_affected_sales_invoice() - - if so_list or dn_list or si_list: - print("Entries with Target Warehouse:") - - if so_list: - print("Sales Order") - print(so_list) - - if dn_list: - print("Delivery Notes") - print([d.name for d in dn_list]) - - if si_list: - print("Sales Invoice") - print([d.name for d in si_list]) - - -def repost(): - dn_failed_list, si_failed_list = [], [] - repost_dn(dn_failed_list) - repost_si(si_failed_list) - repost_so() - frappe.db.commit() - - if dn_failed_list: - print("-"*40) - print("Delivery Note Failed to Repost") - print(dn_failed_list) - - if si_failed_list: - print("-"*40) - print("Sales Invoice Failed to Repost") - print(si_failed_list) - print() - - print(""" -If above Delivery Notes / Sales Invoice failed due to negative stock, follow these steps: - - Ensure that stock is available for those items in the mentioned warehouse on the date mentioned in the error - - Run this patch again -""") - -def repost_dn(dn_failed_list): - dn_list = get_affected_delivery_notes() - - if dn_list: - print("-"*40) - print("Reposting Delivery Notes") - - for dn in dn_list: - if dn.docstatus == 0: - continue - - print(dn.name) - - try: - dn_doc = frappe.get_doc("Delivery Note", dn.name) - dn_doc.docstatus = 2 - dn_doc.update_prevdoc_status() - dn_doc.update_stock_ledger() - dn_doc.cancel_packing_slips() - frappe.db.sql("""delete from `tabGL Entry` - where voucher_type='Delivery Note' and voucher_no=%s""", dn.name) - - frappe.db.sql("update `tabDelivery Note Item` set target_warehouse='' where parent=%s", dn.name) - dn_doc = frappe.get_doc("Delivery Note", dn.name) - dn_doc.docstatus = 1 - dn_doc.on_submit() - frappe.db.commit() - except Exception: - dn_failed_list.append(dn.name) - frappe.local.stockledger_exceptions = None - print(frappe.get_traceback()) - frappe.db.rollback() - - frappe.db.sql("update `tabDelivery Note Item` set target_warehouse='' where docstatus=0") - -def repost_si(si_failed_list): - si_list = get_affected_sales_invoice() - - if si_list: - print("-"*40) - print("Reposting Sales Invoice") - - for si in si_list: - if si.docstatus == 0: - continue - - print(si.name) - - try: - si_doc = frappe.get_doc("Sales Invoice", si.name) - si_doc.docstatus = 2 - si_doc.update_stock_ledger() - frappe.db.sql("""delete from `tabGL Entry` - where voucher_type='Sales Invoice' and voucher_no=%s""", si.name) - - frappe.db.sql("update `tabSales Invoice Item` set target_warehouse='' where parent=%s", si.name) - si_doc = frappe.get_doc("Sales Invoice", si.name) - si_doc.docstatus = 1 - si_doc.update_stock_ledger() - si_doc.make_gl_entries() - frappe.db.commit() - except Exception: - si_failed_list.append(si.name) - frappe.local.stockledger_exceptions = None - print(frappe.get_traceback()) - frappe.db.rollback() - - frappe.db.sql("update `tabSales Invoice Item` set target_warehouse='' where docstatus=0") - -def repost_so(): - so_list = get_affected_sales_order() - - frappe.db.sql("update `tabSales Order Item` set target_warehouse=''") - - if so_list: - print("-"*40) - print("Sales Order reposted") - - -def get_affected_delivery_notes(): - return frappe.db.sql("""select distinct dn.name, dn.docstatus - from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item - where dn.name=dn_item.parent and dn.docstatus < 2 - and dn_item.target_warehouse is not null and dn_item.target_warehouse != '' - order by dn.posting_date asc""", as_dict=1) - -def get_affected_sales_invoice(): - return frappe.db.sql("""select distinct si.name, si.docstatus - from `tabSales Invoice` si, `tabSales Invoice Item` si_item - where si.name=si_item.parent and si.docstatus < 2 and si.update_stock=1 - and si_item.target_warehouse is not null and si_item.target_warehouse != '' - order by si.posting_date asc""", as_dict=1) - -def get_affected_sales_order(): - return frappe.db.sql_list("""select distinct parent from `tabSales Order Item` - where target_warehouse is not null and target_warehouse != '' and docstatus <2""") \ No newline at end of file diff --git a/erpnext/patches/v6_12/set_overdue_tasks.py b/erpnext/patches/v6_12/set_overdue_tasks.py deleted file mode 100644 index 7dbb8ba8b6..0000000000 --- a/erpnext/patches/v6_12/set_overdue_tasks.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Task") - - from erpnext.projects.doctype.task.task import set_tasks_as_overdue - set_tasks_as_overdue() diff --git a/erpnext/patches/v6_16/__init__.py b/erpnext/patches/v6_16/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v6_16/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v6_16/create_manufacturer_records.py b/erpnext/patches/v6_16/create_manufacturer_records.py deleted file mode 100644 index 5ae65f0660..0000000000 --- a/erpnext/patches/v6_16/create_manufacturer_records.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2016, 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 import cstr - -def execute(): - frappe.reload_doc("stock", "doctype", "manufacturer") - frappe.reload_doctype("Item") - - for d in frappe.db.sql("""select distinct manufacturer from tabItem - where ifnull(manufacturer, '') != '' and disabled=0"""): - manufacturer_name = cstr(d[0]).strip() - if manufacturer_name and not frappe.db.exists("Manufacturer", manufacturer_name): - man = frappe.new_doc("Manufacturer") - man.short_name = manufacturer_name - man.full_name = manufacturer_name - man.save() diff --git a/erpnext/patches/v6_16/update_billing_status_in_dn_and_pr.py b/erpnext/patches/v6_16/update_billing_status_in_dn_and_pr.py deleted file mode 100644 index 481f13005b..0000000000 --- a/erpnext/patches/v6_16/update_billing_status_in_dn_and_pr.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - for dt in ("Delivery Note", "Purchase Receipt"): - frappe.reload_doctype(dt) - frappe.reload_doctype(dt + " Item") - - # Update billed_amt in DN and PR which are not against any order - for d in frappe.db.sql("""select name from `tabDelivery Note Item` item - where (so_detail is null or so_detail = '') and docstatus=1""", as_dict=1): - - billed_amt = frappe.db.sql("""select sum(amount) from `tabSales Invoice Item` - where dn_detail=%s and docstatus=1""", d.name) - billed_amt = billed_amt and billed_amt[0][0] or 0 - frappe.db.set_value("Delivery Note Item", d.name, "billed_amt", billed_amt, update_modified=False) - - frappe.db.commit() - - # Update billed_amt in DN and PR which are not against any order - for d in frappe.db.sql("""select name from `tabPurchase Receipt Item` item - where (purchase_order_item is null or purchase_order_item = '') and docstatus=1""", as_dict=1): - - billed_amt = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item` - where pr_detail=%s and docstatus=1""", d.name) - billed_amt = billed_amt and billed_amt[0][0] or 0 - frappe.db.set_value("Purchase Receipt Item", d.name, "billed_amt", billed_amt, update_modified=False) - - frappe.db.commit() - - for dt in ("Delivery Note", "Purchase Receipt"): - # Update billed amt which are against order or invoice - # Update billing status for all - for d in frappe.db.sql("select name from `tab{0}` where docstatus=1".format(dt), as_dict=1): - doc = frappe.get_doc(dt, d.name) - doc.update_billing_status(update_modified=False) - doc.set_status(update=True, update_modified=False) - - frappe.db.commit() diff --git a/erpnext/patches/v6_19/__init__.py b/erpnext/patches/v6_19/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v6_19/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v6_19/comment_feed_communication.py b/erpnext/patches/v6_19/comment_feed_communication.py deleted file mode 100644 index bc41c2d8ff..0000000000 --- a/erpnext/patches/v6_19/comment_feed_communication.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.patches.v6_19.comment_feed_communication import update_timeline_doc_for - -def execute(): - for doctype in ("Customer", "Supplier", "Employee", "Project"): - update_timeline_doc_for(doctype) diff --git a/erpnext/patches/v6_2/__init__.py b/erpnext/patches/v6_2/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v6_2/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v6_2/fix_missing_default_taxes_and_lead.py b/erpnext/patches/v6_2/fix_missing_default_taxes_and_lead.py deleted file mode 100644 index b0cfc3d3bf..0000000000 --- a/erpnext/patches/v6_2/fix_missing_default_taxes_and_lead.py +++ /dev/null @@ -1,25 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - # remove missing lead - for customer in frappe.db.sql_list("""select name from `tabCustomer` - where ifnull(lead_name, '')!='' and not exists (select name from `tabLead` where name=`tabCustomer`.lead_name)"""): - frappe.db.set_value("Customer", customer, "lead_name", None) - - # remove missing default taxes - for customer in frappe.db.sql_list("""select name from `tabCustomer` - where ifnull(default_taxes_and_charges, '')!='' and not exists ( - select name from `tabSales Taxes and Charges Template` where name=`tabCustomer`.default_taxes_and_charges - )"""): - c = frappe.get_doc("Customer", customer) - c.default_taxes_and_charges = None - c.save() - - for supplier in frappe.db.sql_list("""select name from `tabSupplier` - where ifnull(default_taxes_and_charges, '')!='' and not exists ( - select name from `tabPurchase Taxes and Charges Template` where name=`tabSupplier`.default_taxes_and_charges - )"""): - c = frappe.get_doc("Supplier", supplier) - c.default_taxes_and_charges = None - c.save() diff --git a/erpnext/patches/v6_2/remove_newsletter_duplicates.py b/erpnext/patches/v6_2/remove_newsletter_duplicates.py deleted file mode 100644 index f9d15475d1..0000000000 --- a/erpnext/patches/v6_2/remove_newsletter_duplicates.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - duplicates = frappe.db.sql("""select email_group, email, count(name) - from `tabEmail Group Member` - group by email_group, email - having count(name) > 1""") - - # delete all duplicates except 1 - for email_group, email, count in duplicates: - frappe.db.sql("""delete from `tabEmail Group Member` - where email_group=%s and email=%s limit %s""", (email_group, email, count-1)) diff --git a/erpnext/patches/v6_20/__init__.py b/erpnext/patches/v6_20/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v6_20/set_party_account_currency_in_orders.py b/erpnext/patches/v6_20/set_party_account_currency_in_orders.py deleted file mode 100644 index ae7ad9592d..0000000000 --- a/erpnext/patches/v6_20/set_party_account_currency_in_orders.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - for doctype in ("Sales Order", "Purchase Order"): - frappe.reload_doctype(doctype) - - for order in frappe.db.sql("""select name, {0} as party from `tab{1}` - where advance_paid > 0 and docstatus=1""" - .format(("customer" if doctype=="Sales Order" else "supplier"), doctype), as_dict=1): - - party_account_currency = frappe.db.get_value("Journal Entry Account", { - "reference_type": doctype, - "reference_name": order.name, - "party": order.party, - "docstatus": 1, - "is_advance": "Yes" - }, "account_currency") - - frappe.db.set_value(doctype, order.name, "party_account_currency", party_account_currency) - \ No newline at end of file diff --git a/erpnext/patches/v6_20x/__init__.py b/erpnext/patches/v6_20x/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v6_20x/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v6_20x/remove_customer_supplier_roles.py b/erpnext/patches/v6_20x/remove_customer_supplier_roles.py deleted file mode 100644 index a651576887..0000000000 --- a/erpnext/patches/v6_20x/remove_customer_supplier_roles.py +++ /dev/null @@ -1,23 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("buying", "doctype", "request_for_quotation_supplier") - frappe.reload_doc("buying", "doctype", "request_for_quotation_item") - frappe.reload_doc("buying", "doctype", "request_for_quotation") - frappe.reload_doc("projects", "doctype", "timesheet") - - for role in ('Customer', 'Supplier'): - frappe.db.sql('''delete from `tabHas Role` - where role=%s and parent in ("Administrator", "Guest")''', role) - - if not frappe.db.sql('select name from `tabHas Role` where role=%s', role): - - # delete DocPerm - for doctype in frappe.db.sql('select parent from tabDocPerm where role=%s', role): - d = frappe.get_doc("DocType", doctype[0]) - d.permissions = [p for p in d.permissions if p.role != role] - d.save() - - # delete Role - frappe.delete_doc_if_exists('Role', role) diff --git a/erpnext/patches/v6_20x/remove_fiscal_year_from_holiday_list.py b/erpnext/patches/v6_20x/remove_fiscal_year_from_holiday_list.py deleted file mode 100644 index d440c6859e..0000000000 --- a/erpnext/patches/v6_20x/remove_fiscal_year_from_holiday_list.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Holiday List") - - default_holiday_list = frappe.db.get_value("Holiday List", {"is_default": 1}) - if default_holiday_list: - for company in frappe.get_all("Company", fields=["name", "default_holiday_list"]): - if not company.default_holiday_list: - frappe.db.set_value("Company", company.name, "default_holiday_list", default_holiday_list) - - - fiscal_years = frappe._dict((fy.name, fy) for fy in frappe.get_all("Fiscal Year", fields=["name", "year_start_date", "year_end_date"])) - - for holiday_list in frappe.get_all("Holiday List", fields=["name", "fiscal_year"]): - fy = fiscal_years[holiday_list.fiscal_year] - frappe.db.set_value("Holiday List", holiday_list.name, "from_date", fy.year_start_date) - frappe.db.set_value("Holiday List", holiday_list.name, "to_date", fy.year_end_date) diff --git a/erpnext/patches/v6_20x/rename_project_name_to_project.py b/erpnext/patches/v6_20x/rename_project_name_to_project.py deleted file mode 100644 index 49ec9d22bc..0000000000 --- a/erpnext/patches/v6_20x/rename_project_name_to_project.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import rename_field - -def execute(): - - doc_list = ["Work Order", "BOM", "Purchase Invoice Item", "Sales Invoice", - "Purchase Order Item", "Stock Entry", "Delivery Note", "Sales Order", - "Purchase Receipt Item", "Supplier Quotation Item"] - - for doctype in doc_list: - frappe.reload_doctype(doctype) - rename_field(doctype, "project_name", "project") - \ No newline at end of file diff --git a/erpnext/patches/v6_20x/repost_valuation_rate_for_negative_inventory.py b/erpnext/patches/v6_20x/repost_valuation_rate_for_negative_inventory.py deleted file mode 100644 index 8369fea317..0000000000 --- a/erpnext/patches/v6_20x/repost_valuation_rate_for_negative_inventory.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from frappe.utils import cint -from erpnext.stock.stock_balance import repost - -def execute(): - if cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")): - repost(only_actual=True) \ No newline at end of file diff --git a/erpnext/patches/v6_20x/set_compact_print.py b/erpnext/patches/v6_20x/set_compact_print.py deleted file mode 100644 index 495407f0e0..0000000000 --- a/erpnext/patches/v6_20x/set_compact_print.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals -import frappe - -from erpnext.setup.install import create_compact_item_print_custom_field - -def execute(): - create_compact_item_print_custom_field() - frappe.db.set_value("Print Settings", None, "compact_item_print", 1) diff --git a/erpnext/patches/v6_20x/update_product_bundle_description.py b/erpnext/patches/v6_20x/update_product_bundle_description.py deleted file mode 100644 index 1fac44b001..0000000000 --- a/erpnext/patches/v6_20x/update_product_bundle_description.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.utils import sanitize_html - -def execute(): - for product_bundle in frappe.get_all('Product Bundle'): - doc = frappe.get_doc('Product Bundle', product_bundle.name) - for item in doc.items: - if item.description: - description = sanitize_html(item.description) - item.db_set('description', description, update_modified=False) diff --git a/erpnext/patches/v6_21/__init__.py b/erpnext/patches/v6_21/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v6_21/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v6_21/fix_reorder_level.py b/erpnext/patches/v6_21/fix_reorder_level.py deleted file mode 100644 index 82a35ebab1..0000000000 --- a/erpnext/patches/v6_21/fix_reorder_level.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import unicode_literals - -import frappe -from erpnext.stock.doctype.item.item import DuplicateReorderRows - -def execute(): - if frappe.db.has_column("Item", "re_order_level"): - for item in frappe.db.sql("""select name, default_warehouse, re_order_level, re_order_qty - from tabItem - where ifnull(re_order_level, 0) != 0 - and ifnull(re_order_qty, 0) != 0""", as_dict=1): - - item_doc = frappe.get_doc("Item", item.name) - item_doc.append("reorder_levels", { - "warehouse": item.default_warehouse, - "warehouse_reorder_level": item.re_order_level, - "warehouse_reorder_qty": item.re_order_qty, - "material_request_type": "Purchase" - }) - - try: - item_doc.save() - except DuplicateReorderRows: - pass diff --git a/erpnext/patches/v6_21/rename_material_request_fields.py b/erpnext/patches/v6_21/rename_material_request_fields.py deleted file mode 100644 index 07be27a5d6..0000000000 --- a/erpnext/patches/v6_21/rename_material_request_fields.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import rename_field - -def execute(): - frappe.reload_doc('stock', 'doctype', 'material_request_item') - rename_field("Material Request Item", "sales_order_no", "sales_order") - - frappe.reload_doc('support', 'doctype', 'maintenance_schedule_item') - rename_field("Maintenance Schedule Item", "prevdoc_docname", "sales_order") - \ No newline at end of file diff --git a/erpnext/patches/v6_23/__init__.py b/erpnext/patches/v6_23/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v6_23/update_stopped_status_to_closed.py b/erpnext/patches/v6_23/update_stopped_status_to_closed.py deleted file mode 100644 index 79d1e0ac30..0000000000 --- a/erpnext/patches/v6_23/update_stopped_status_to_closed.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - for dt in ("Sales Order", "Purchase Order"): - frappe.db.sql("update `tab{0}` set status='Closed' where status='Stopped'".format(dt)) \ No newline at end of file diff --git a/erpnext/patches/v6_24/__init__.py b/erpnext/patches/v6_24/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v6_24/map_customer_address_to_shipping_address_on_po.py b/erpnext/patches/v6_24/map_customer_address_to_shipping_address_on_po.py deleted file mode 100644 index 1dd8083c7c..0000000000 --- a/erpnext/patches/v6_24/map_customer_address_to_shipping_address_on_po.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Purchase Order") - - if not frappe.db.has_column("Purchase Order", "shipping_address"): - return - - if not frappe.db.has_column("Purchase Order", "customer_address"): - return - - frappe.db.sql("""update `tabPurchase Order` set shipping_address=customer_address, - shipping_address_display=customer_address_display""") - - frappe.db.commit() \ No newline at end of file diff --git a/erpnext/patches/v6_24/set_recurring_id.py b/erpnext/patches/v6_24/set_recurring_id.py deleted file mode 100644 index 527a2fd3d9..0000000000 --- a/erpnext/patches/v6_24/set_recurring_id.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - for doctype in ('Sales Order', 'Purchase Order', 'Sales Invoice', - 'Purchase Invoice'): - frappe.reload_doctype(doctype) - frappe.db.sql('''update `tab{0}` set submit_on_creation=1, notify_by_email=1 - where is_recurring=1'''.format(doctype)) - frappe.db.sql('''update `tab{0}` set notify_by_email=1 - where is_recurring=1'''.format(doctype)) - frappe.db.sql('''update `tab{0}` set recurring_id = name - where is_recurring=1 and ifnull(recurring_id, '') = "" '''.format(doctype)) diff --git a/erpnext/patches/v6_27/__init__.py b/erpnext/patches/v6_27/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v6_27/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v6_27/fix_recurring_order_status.py b/erpnext/patches/v6_27/fix_recurring_order_status.py deleted file mode 100644 index 5843c9fbe5..0000000000 --- a/erpnext/patches/v6_27/fix_recurring_order_status.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - for doc in ( - { - "doctype": "Sales Order", - "stock_doctype": "Delivery Note", - "invoice_doctype": "Sales Invoice", - "stock_doctype_ref_field": "against_sales_order", - "invoice_ref_field": "sales_order", - "qty_field": "delivered_qty" - }, - { - "doctype": "Purchase Order", - "stock_doctype": "Purchase Receipt", - "invoice_doctype": "Purchase Invoice", - "stock_doctype_ref_field": "prevdoc_docname", - "invoice_ref_field": "purchase_order", - "qty_field": "received_qty" - }): - - order_list = frappe.db.sql("""select name from `tab{0}` - where docstatus=1 and is_recurring=1 - and ifnull(recurring_id, '') != name and creation >= '2016-01-25'""" - .format(doc["doctype"]), as_dict=1) - - for order in order_list: - frappe.db.sql("""update `tab{0} Item` - set {1}=0, billed_amt=0 where parent=%s""".format(doc["doctype"], - doc["qty_field"]), order.name) - - # Check against Delivery Note and Purchase Receipt - stock_doc_list = frappe.db.sql("""select distinct parent from `tab{0} Item` - where docstatus=1 and ifnull({1}, '')=%s""" - .format(doc["stock_doctype"], doc["stock_doctype_ref_field"]), order.name) - - if stock_doc_list: - for dn in stock_doc_list: - frappe.get_doc(doc["stock_doctype"], dn[0]).update_qty(update_modified=False) - - # Check against Invoice - invoice_list = frappe.db.sql("""select distinct parent from `tab{0} Item` - where docstatus=1 and ifnull({1}, '')=%s""" - .format(doc["invoice_doctype"], doc["invoice_ref_field"]), order.name) - - if invoice_list: - for dn in invoice_list: - frappe.get_doc(doc["invoice_doctype"], dn[0]).update_qty(update_modified=False) - - frappe.get_doc(doc["doctype"], order.name).set_status(update=True, update_modified=False) \ No newline at end of file diff --git a/erpnext/patches/v6_3/__init__.py b/erpnext/patches/v6_3/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v6_3/convert_applicable_territory.py b/erpnext/patches/v6_3/convert_applicable_territory.py deleted file mode 100644 index 231a483ea2..0000000000 --- a/erpnext/patches/v6_3/convert_applicable_territory.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("stock", "doctype", "price_list_country") - frappe.reload_doc("accounts", "doctype", "shipping_rule_country") - frappe.reload_doctype("Price List") - frappe.reload_doctype("Shipping Rule") - frappe.reload_doctype("shopping_cart", "doctype", "shopping_cart_settings") - - # for price list - countries = frappe.db.sql_list("select name from tabCountry") - - for doctype in ("Price List", "Shipping Rule"): - for at in frappe.db.sql("""select name, parent, territory from `tabApplicable Territory` where - parenttype = %s """, doctype, as_dict=True): - if at.territory in countries: - parent = frappe.get_doc(doctype, at.parent) - if not parent.countries: - parent.append("countries", {"country": at.territory}) - parent.save() - - - frappe.delete_doc("DocType", "Applicable Territory") diff --git a/erpnext/patches/v6_4/__init__.py b/erpnext/patches/v6_4/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v6_4/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v6_4/email_digest_update.py b/erpnext/patches/v6_4/email_digest_update.py deleted file mode 100644 index 8342b7fce6..0000000000 --- a/erpnext/patches/v6_4/email_digest_update.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Email Digest") - frappe.db.sql("""update `tabEmail Digest` set expense_year_to_date = - income_year_to_date""") - - if frappe.db.exists("Email Digest", "Scheduler Errors"): - frappe.delete_doc("Email Digest", "Scheduler Errors") diff --git a/erpnext/patches/v6_4/fix_duplicate_bins.py b/erpnext/patches/v6_4/fix_duplicate_bins.py deleted file mode 100644 index 77d05273e8..0000000000 --- a/erpnext/patches/v6_4/fix_duplicate_bins.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from erpnext.stock.stock_balance import repost_stock - -def execute(): - bins = frappe.db.sql("""select item_code, warehouse, count(*) from `tabBin` - group by item_code, warehouse having count(*) > 1""", as_dict=True) - - for d in bins: - try: - frappe.db.sql("delete from tabBin where item_code=%s and warehouse=%s", (d.item_code, d.warehouse)) - - repost_stock(d.item_code, d.warehouse, allow_zero_rate=True, only_actual=False, only_bin=True) - - frappe.db.commit() - except: - frappe.db.rollback() \ No newline at end of file diff --git a/erpnext/patches/v6_4/fix_expense_included_in_valuation.py b/erpnext/patches/v6_4/fix_expense_included_in_valuation.py deleted file mode 100644 index 7ed15ab010..0000000000 --- a/erpnext/patches/v6_4/fix_expense_included_in_valuation.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe -from frappe.utils import cstr - -def execute(): - for company in frappe.db.sql("select name, expenses_included_in_valuation from tabCompany", as_dict=1): - frozen_date = get_frozen_date(company.name, company.expenses_included_in_valuation) - - # Purchase Invoices after frozen date - # which are not against Receipt, but valuation related tax is there - pi_list = frappe.db.sql(""" - select distinct pi.name - from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item - where - pi.name = pi_item.parent - and pi.company = %s - and pi.posting_date > %s - and pi.docstatus = 1 - and pi.is_opening = 'No' - and (pi_item.item_tax_amount is not null and pi_item.item_tax_amount > 0) - and (pi_item.purchase_receipt is null or pi_item.purchase_receipt = '') - and (pi_item.item_code is not null and pi_item.item_code != '') - and exists(select name from `tabItem` where name=pi_item.item_code and is_stock_item=1) - """, (company.name, frozen_date), as_dict=1) - - for pi in pi_list: - # Check whether gle exists for Expenses Included in Valuation account against the PI - gle_for_expenses_included_in_valuation = frappe.db.sql("""select name from `tabGL Entry` - where voucher_type='Purchase Invoice' and voucher_no=%s and account=%s""", - (pi.name, company.expenses_included_in_valuation)) - - if gle_for_expenses_included_in_valuation: - print(pi.name) - - frappe.db.sql("""delete from `tabGL Entry` - where voucher_type='Purchase Invoice' and voucher_no=%s""", pi.name) - - purchase_invoice = frappe.get_doc("Purchase Invoice", pi.name) - - # some old entries have missing expense accounts - if purchase_invoice.against_expense_account: - expense_account = purchase_invoice.against_expense_account.split(",") - if len(expense_account) == 1: - expense_account = expense_account[0] - for item in purchase_invoice.items: - if not item.expense_account: - item.db_set("expense_account", expense_account, update_modified=False) - - purchase_invoice.make_gl_entries() - -def get_frozen_date(company, account): - # Accounting frozen upto - accounts_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto") - - # Last adjustment entry to correct Expenses Included in Valuation account balance - last_adjustment_entry = frappe.db.sql("""select posting_date from `tabGL Entry` - where account=%s and company=%s and voucher_type = 'Journal Entry' - order by posting_date desc limit 1""", (account, company)) - - last_adjustment_date = cstr(last_adjustment_entry[0][0]) if last_adjustment_entry else None - - # Last period closing voucher - last_closing_entry = frappe.db.sql("""select posting_date from `tabGL Entry` - where company=%s and voucher_type = 'Period Closing Voucher' - order by posting_date desc limit 1""", company) - - last_closing_date = cstr(last_closing_entry[0][0]) if last_closing_entry else None - - frozen_date = max([accounts_frozen_upto, last_adjustment_date, last_closing_date]) - - return frozen_date or '1900-01-01' diff --git a/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py b/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py deleted file mode 100644 index b53412d7eb..0000000000 --- a/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Sales Invoice Advance") - frappe.reload_doctype("Purchase Invoice Advance") - - je_rows = frappe.db.sql(""" - select name, parent, reference_type, reference_name, debit, credit - from `tabJournal Entry Account` - where docstatus=1 and date(modified) >= '2015-09-17' - and ((ifnull(debit_in_account_currency, 0)*exchange_rate != ifnull(debit, 0)) - or (ifnull(credit_in_account_currency, 0)*exchange_rate != ifnull(credit, 0))) - order by parent - """, as_dict=True) - - journal_entries = [] - - for d in je_rows: - if d.parent not in journal_entries: - journal_entries.append(d.parent) - - is_advance_entry=None - if d.reference_type in ("Sales Invoice", "Purchase Invoice") and d.reference_name: - is_advance_entry = frappe.db.sql("""select name from `tab{0}` - where reference_name=%s and reference_row=%s - and ifnull(allocated_amount, 0) > 0 and docstatus=1""" - .format(d.reference_type + " Advance"), (d.parent, d.name)) - - if is_advance_entry or not (d.debit or d.credit): - frappe.db.sql(""" - update `tabJournal Entry Account` - set debit=debit_in_account_currency*exchange_rate, - credit=credit_in_account_currency*exchange_rate - where name=%s""", d.name) - else: - frappe.db.sql(""" - update `tabJournal Entry Account` - set debit_in_account_currency=debit/exchange_rate, - credit_in_account_currency=credit/exchange_rate - where name=%s""", d.name) - - for d in journal_entries: - print(d) - # delete existing gle - frappe.db.sql("delete from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s", d) - - # repost gl entries - je = frappe.get_doc("Journal Entry", d) - je.make_gl_entries() \ No newline at end of file diff --git a/erpnext/patches/v6_4/fix_modified_in_sales_order_and_purchase_order.py b/erpnext/patches/v6_4/fix_modified_in_sales_order_and_purchase_order.py deleted file mode 100644 index f27489e7b0..0000000000 --- a/erpnext/patches/v6_4/fix_modified_in_sales_order_and_purchase_order.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - for doctype in ("Sales Order", "Purchase Order"): - data = frappe.db.sql("""select parent, modified_by, modified - from `tab{doctype} Item` where docstatus=1 group by parent""".format(doctype=doctype), as_dict=True) - for item in data: - frappe.db.sql("""update `tab{doctype}` set modified_by=%(modified_by)s, modified=%(modified)s - where name=%(parent)s""".format(doctype=doctype), item) diff --git a/erpnext/patches/v6_4/fix_sales_order_maintenance_status.py b/erpnext/patches/v6_4/fix_sales_order_maintenance_status.py deleted file mode 100644 index 50aa9e542e..0000000000 --- a/erpnext/patches/v6_4/fix_sales_order_maintenance_status.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - for doc in frappe.get_all("Sales Order", filters={"docstatus": 1, - "order_type": "Maintenance"}): - doc = frappe.get_doc("Sales Order", doc.name) - doc.set_status(update=True) diff --git a/erpnext/patches/v6_4/fix_status_in_sales_and_purchase_order.py b/erpnext/patches/v6_4/fix_status_in_sales_and_purchase_order.py deleted file mode 100644 index 746a99004a..0000000000 --- a/erpnext/patches/v6_4/fix_status_in_sales_and_purchase_order.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - for doctype in ("Sales Order", "Purchase Order"): - for doc in frappe.get_all(doctype, filters={"docstatus": 1}): - doc = frappe.get_doc(doctype, doc.name) - doc.set_status(update=True) diff --git a/erpnext/patches/v6_4/make_image_thumbnail.py b/erpnext/patches/v6_4/make_image_thumbnail.py deleted file mode 100644 index 2c86e8af86..0000000000 --- a/erpnext/patches/v6_4/make_image_thumbnail.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import print_function, unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("File") - frappe.reload_doctype("Item") - for item in frappe.get_all("Item", fields=("name", "website_image", "thumbnail")): - if item.website_image and not item.thumbnail: - item_doc = frappe.get_doc("Item", item.name) - try: - item_doc.make_thumbnail() - if item_doc.thumbnail: - item_doc.db_set("thumbnail", item_doc.thumbnail, update_modified=False) - except Exception: - print("Unable to make thumbnail for {0}".format(item.website_image.encode("utf-8"))) diff --git a/erpnext/patches/v6_4/repost_gle_for_journal_entries_where_reference_name_missing.py b/erpnext/patches/v6_4/repost_gle_for_journal_entries_where_reference_name_missing.py deleted file mode 100644 index 1319b53558..0000000000 --- a/erpnext/patches/v6_4/repost_gle_for_journal_entries_where_reference_name_missing.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe - -def execute(): - je_list = frappe.db.sql_list("""select distinct parent from `tabJournal Entry Account` je - where docstatus=1 and ifnull(reference_name, '') !='' and creation > '2015-03-01' - and not exists(select name from `tabGL Entry` - where voucher_type='Journal Entry' and voucher_no=je.parent - and against_voucher_type=je.reference_type - and against_voucher=je.reference_name)""") - - for d in je_list: - print(d) - - # delete existing gle - frappe.db.sql("delete from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s", d) - - # repost gl entries - je = frappe.get_doc("Journal Entry", d) - je.make_gl_entries() \ No newline at end of file diff --git a/erpnext/patches/v6_4/round_status_updater_percentages.py b/erpnext/patches/v6_4/round_status_updater_percentages.py deleted file mode 100644 index 900e906b7b..0000000000 --- a/erpnext/patches/v6_4/round_status_updater_percentages.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - for doctype, fieldname in ( - ("Sales Order", "per_billed"), - ("Sales Order", "per_delivered"), - ("Delivery Note", "per_installed"), - ("Purchase Order", "per_billed"), - ("Purchase Order", "per_received"), - ("Material Request", "per_ordered"), - ): - frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=round(`{fieldname}`, 2)""".format( - doctype=doctype, fieldname=fieldname)) diff --git a/erpnext/patches/v6_4/set_user_in_contact.py b/erpnext/patches/v6_4/set_user_in_contact.py deleted file mode 100644 index 7e8a6eecd5..0000000000 --- a/erpnext/patches/v6_4/set_user_in_contact.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Contact") - frappe.db.sql("""update tabContact, tabUser set tabContact.user = tabUser.name - where tabContact.email_id = tabUser.email""") diff --git a/erpnext/patches/v6_5/__init__.py b/erpnext/patches/v6_5/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v6_5/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v6_5/show_in_website_for_template_item.py b/erpnext/patches/v6_5/show_in_website_for_template_item.py deleted file mode 100644 index af6e8304d6..0000000000 --- a/erpnext/patches/v6_5/show_in_website_for_template_item.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import unicode_literals -import frappe -import frappe.website.render - -def execute(): - for item_code in frappe.db.sql_list("""select distinct variant_of from `tabItem` - where variant_of is not null and variant_of !='' and show_in_website=1"""): - - item = frappe.get_doc("Item", item_code) - item.db_set("show_in_website", 1, update_modified=False) - - item.make_route() - item.db_set("route", item.route, update_modified=False) - - frappe.website.render.clear_cache() diff --git a/erpnext/patches/v6_6/__init__.py b/erpnext/patches/v6_6/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v6_6/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v6_6/fix_website_image.py b/erpnext/patches/v6_6/fix_website_image.py deleted file mode 100644 index cc3e2d852c..0000000000 --- a/erpnext/patches/v6_6/fix_website_image.py +++ /dev/null @@ -1,32 +0,0 @@ -from __future__ import print_function, unicode_literals -import frappe -from frappe.utils import encode - -def execute(): - """Fix the File records created via item.py even if the website_image file didn't exist""" - for item in frappe.db.sql_list("""select name from `tabItem` - where website_image is not null and website_image != '' - and website_image like '/files/%' - and exists ( - select name from `tabFile` - where attached_to_doctype='Item' - and attached_to_name=`tabItem`.name - and file_url=`tabItem`.website_image - and (file_name is null or file_name = '') - )"""): - - item = frappe.get_doc("Item", item) - file = frappe.get_doc("File", { - "attached_to_doctype": "Item", - "attached_to_name": item.name, - "file_url": item.website_image - }) - - try: - file.validate_file() - except IOError: - print(encode(item.website_image), "does not exist") - file.delete() - item.db_set("website_image", None, update_modified=False) - - diff --git a/erpnext/patches/v6_6/remove_fiscal_year_from_leave_allocation.py b/erpnext/patches/v6_6/remove_fiscal_year_from_leave_allocation.py deleted file mode 100644 index 11c582fc49..0000000000 --- a/erpnext/patches/v6_6/remove_fiscal_year_from_leave_allocation.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Leave Allocation") - if frappe.db.has_column("Leave Allocation", "fiscal_year"): - for leave_allocation in frappe.db.sql("select name, fiscal_year from `tabLeave Allocation`", as_dict=True): - dates = frappe.db.get_value("Fiscal Year", leave_allocation["fiscal_year"], - ["year_start_date", "year_end_date"]) - - if dates: - year_start_date, year_end_date = dates - - frappe.db.sql("""update `tabLeave Allocation` - set from_date=%s, to_date=%s where name=%s""", - (year_start_date, year_end_date, leave_allocation["name"])) - diff --git a/erpnext/patches/v6_8/__init__.py b/erpnext/patches/v6_8/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v6_8/make_webform_standard.py b/erpnext/patches/v6_8/make_webform_standard.py deleted file mode 100644 index 2cc16a286f..0000000000 --- a/erpnext/patches/v6_8/make_webform_standard.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - pass - - # done via fixtures - - # frappe.reload_doctype("Web Form") - # frappe.delete_doc("Web Form", "Issues") - # frappe.delete_doc("Web Form", "Addresses") - - # from erpnext.setup.install import add_web_forms - # add_web_forms() diff --git a/erpnext/patches/v6_8/move_drop_ship_to_po_items.py b/erpnext/patches/v6_8/move_drop_ship_to_po_items.py deleted file mode 100644 index 7184deeccc..0000000000 --- a/erpnext/patches/v6_8/move_drop_ship_to_po_items.py +++ /dev/null @@ -1,43 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Purchase Order") - frappe.reload_doctype("Purchase Order Item") - - if not frappe.db.has_column("Purchase Order", "delivered_by_supplier"): - return - - for po in frappe.get_all("Purchase Order", filters={"delivered_by_supplier": 1}, fields=["name"]): - purchase_order = frappe.get_doc("Purchase Order", po) - - for item in purchase_order.items: - if item.prevdoc_doctype == "Sales Order": - delivered_by_supplier = frappe.get_value("Sales Order Item", item.prevdoc_detail_docname, - "delivered_by_supplier") - - if delivered_by_supplier: - frappe.db.sql("""update `tabPurchase Order Item` - set delivered_by_supplier=1, billed_amt=amount, received_qty=qty - where name=%s """, item.name) - - update_per_received(purchase_order) - update_per_billed(purchase_order) - -def update_per_received(po): - frappe.db.sql(""" update `tabPurchase Order` - set per_received = round((select sum(if(qty > ifnull(received_qty, 0), - ifnull(received_qty, 0), qty)) / sum(qty) *100 - from `tabPurchase Order Item` - where parent = %(name)s), 2) - where name = %(name)s """, {"name": po.name}) - -def update_per_billed(po): - frappe.db.sql(""" update `tabPurchase Order` - set per_billed = round((select sum( if(amount > ifnull(billed_amt, 0), - ifnull(billed_amt, 0), amount)) / sum(amount) *100 - from `tabPurchase Order Item` - where parent = %(name)s), 2) - where name = %(name)s """, {"name": po.name}) - - diff --git a/erpnext/patches/v7_0/__init__.py b/erpnext/patches/v7_0/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v7_0/calculate_total_costing_amount.py b/erpnext/patches/v7_0/calculate_total_costing_amount.py deleted file mode 100644 index 8ed60a2955..0000000000 --- a/erpnext/patches/v7_0/calculate_total_costing_amount.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.utils import flt - -def execute(): - frappe.reload_doc('projects', 'doctype', 'timesheet') - - for data in frappe.get_all('Timesheet', fields=["name, total_costing_amount"], - filters = [["docstatus", "<", "2"]]): - if flt(data.total_costing_amount) == 0.0: - ts = frappe.get_doc('Timesheet', data.name) - ts.update_cost() - ts.calculate_total_amounts() - ts.flags.ignore_validate = True - ts.flags.ignore_mandatory = True - ts.flags.ignore_validate_update_after_submit = True - ts.flags.ignore_links = True - ts.save() diff --git a/erpnext/patches/v7_0/convert_timelog_to_timesheet.py b/erpnext/patches/v7_0/convert_timelog_to_timesheet.py deleted file mode 100644 index 8c60b5b71e..0000000000 --- a/erpnext/patches/v7_0/convert_timelog_to_timesheet.py +++ /dev/null @@ -1,69 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('projects', 'doctype', 'task') - frappe.reload_doc('projects', 'doctype', 'timesheet') - if not frappe.db.table_exists("Time Log"): - return - - from erpnext.manufacturing.doctype.work_order.work_order \ - import make_timesheet, add_timesheet_detail - - for data in frappe.db.sql("select * from `tabTime Log`", as_dict=1): - if data.task: - company = frappe.db.get_value("Task", data.task, "company") - elif data.work_order: - company = frappe.db.get_value("Work Order", data.work_order, "company") - else: - company = frappe.db.get_single_value('Global Defaults', 'default_company') - - time_sheet = make_timesheet(data.work_order, company) - args = get_timelog_data(data) - add_timesheet_detail(time_sheet, args) - if data.docstatus == 2: - time_sheet.docstatus = 0 - else: - time_sheet.docstatus = data.docstatus - time_sheet.employee = data.employee - time_sheet.note = data.note - time_sheet.company = company - - time_sheet.set_status() - time_sheet.set_dates() - time_sheet.update_cost() - time_sheet.calculate_total_amounts() - time_sheet.flags.ignore_validate = True - time_sheet.flags.ignore_links = True - time_sheet.save(ignore_permissions=True) - - # To ignore validate_mandatory_fields function - if data.docstatus == 1: - time_sheet.db_set("docstatus", 1) - for d in time_sheet.get("time_logs"): - d.db_set("docstatus", 1) - time_sheet.update_work_order(time_sheet.name) - time_sheet.update_task_and_project() - if data.docstatus == 2: - time_sheet.db_set("docstatus", 2) - for d in time_sheet.get("time_logs"): - d.db_set("docstatus", 2) - -def get_timelog_data(data): - return { - 'is_billable': data.billable, - 'from_time': data.from_time, - 'hours': data.hours, - 'to_time': data.to_time, - 'project': data.project, - 'task': data.task, - 'activity_type': data.activity_type, - 'operation': data.operation, - 'operation_id': data.operation_id, - 'workstation': data.workstation, - 'completed_qty': data.completed_qty, - 'billing_rate': data.billing_rate, - 'billing_amount': data.billing_amount, - 'costing_rate': data.costing_rate, - 'costing_amount': data.costing_amount - } diff --git a/erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py b/erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py deleted file mode 100644 index e78f163e07..0000000000 --- a/erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py +++ /dev/null @@ -1,32 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.utils import cint - -def execute(): - if not frappe.db.exists("DocType", "Time Log Batch"): - return - - from erpnext.manufacturing.doctype.work_order.work_order import add_timesheet_detail - - for tlb in frappe.get_all('Time Log Batch', fields=["*"], - filters = [["docstatus", "<", "2"]]): - time_sheet = frappe.new_doc('Timesheet') - time_sheet.employee= "" - time_sheet.company = frappe.db.get_single_value('Global Defaults', 'default_company') - time_sheet.sales_invoice = tlb.sales_invoice - - for data in frappe.get_all('Time Log Batch Detail', fields=["*"], - filters = {'parent': tlb.name}): - args = get_timesheet_data(data) - add_timesheet_detail(time_sheet, args) - - time_sheet.docstatus = tlb.docstatus - time_sheet.flags.ignore_links = True - time_sheet.save(ignore_permissions=True) - -def get_timesheet_data(data): - from erpnext.patches.v7_0.convert_timelog_to_timesheet import get_timelog_data - - time_log = frappe.get_all('Time Log', fields=["*"], filters = {'name': data.time_log}) - if time_log: - return get_timelog_data(time_log[0]) \ No newline at end of file diff --git a/erpnext/patches/v7_0/create_budget_record.py b/erpnext/patches/v7_0/create_budget_record.py deleted file mode 100644 index fd8bec9f32..0000000000 --- a/erpnext/patches/v7_0/create_budget_record.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import unicode_literals -import frappe - -from erpnext.accounts.doctype.budget.budget import DuplicateBudgetError - -def execute(): - frappe.reload_doc("accounts", "doctype", "budget") - frappe.reload_doc("accounts", "doctype", "budget_account") - - existing_budgets = frappe.db.sql(""" - select - cc.name, cc.company, cc.distribution_id, - budget.account, budget.budget_allocated, budget.fiscal_year - from - `tabCost Center` cc, `tabBudget Detail` budget - where - cc.name=budget.parent - """, as_dict=1) - - actions = {} - for d in frappe.db.sql("select name, yearly_bgt_flag, monthly_bgt_flag from tabCompany", as_dict=1): - actions.setdefault(d.name, d) - - budget_records = [] - for d in existing_budgets: - budget = frappe.db.get_value("Budget", - {"cost_center": d.name, "fiscal_year": d.fiscal_year, "company": d.company}) - - if not budget: - budget = frappe.new_doc("Budget") - budget.cost_center = d.name - budget.fiscal_year = d.fiscal_year - budget.monthly_distribution = d.distribution_id - budget.company = d.company - if actions[d.company]["yearly_bgt_flag"]: - budget.action_if_annual_budget_exceeded = actions[d.company]["yearly_bgt_flag"] - if actions[d.company]["monthly_bgt_flag"]: - budget.action_if_accumulated_monthly_budget_exceeded = actions[d.company]["monthly_bgt_flag"] - else: - budget = frappe.get_doc("Budget", budget) - - budget.append("accounts", { - "account": d.account, - "budget_amount": d.budget_allocated - }) - - try: - budget.insert() - budget_records.append(budget) - except DuplicateBudgetError: - pass - - for budget in budget_records: - budget.submit() - - if frappe.db.get_value("DocType", "Budget Detail"): - frappe.delete_doc("DocType", "Budget Detail") \ No newline at end of file diff --git a/erpnext/patches/v7_0/create_warehouse_nestedset.py b/erpnext/patches/v7_0/create_warehouse_nestedset.py deleted file mode 100644 index 1c9fc32142..0000000000 --- a/erpnext/patches/v7_0/create_warehouse_nestedset.py +++ /dev/null @@ -1,128 +0,0 @@ - -from __future__ import unicode_literals -import frappe, erpnext -from frappe import _ -from frappe.utils import cint -from frappe.utils.nestedset import rebuild_tree - -def execute(): - """ - Patch Reference: - 1. check whether warehouse is associated to company or not - 2. if warehouse is associated with company - a. create warehouse group for company - b. set warehouse group as parent to other warehouses and set is_group as 0 - 3. if warehouses is not associated with company - a. get distinct companies from stock ledger entries - b. if sle have only company, - i. set default company to all warehouse - ii. repeat 2.a and 2.b - c. if have multiple companies, - i. create group warehouse without company - ii. repeat 2.b - """ - - frappe.reload_doc("stock", "doctype", "warehouse") - - if check_is_warehouse_associated_with_company(): - for company in frappe.get_all("Company", fields=["name", "abbr"]): - make_warehouse_nestedset(company) - else: - sle_against_companies = frappe.db.sql_list("""select distinct company from `tabStock Ledger Entry`""") - - if len(sle_against_companies) == 1: - company = frappe.get_cached_value('Company', sle_against_companies[0], - fieldname=["name", "abbr"], as_dict=1) - set_company_to_warehouse(company.name) - make_warehouse_nestedset(company) - - elif len(sle_against_companies) > 1: - make_warehouse_nestedset() - -def check_is_warehouse_associated_with_company(): - warehouse_associcated_with_company = False - - for warehouse in frappe.get_all("Warehouse", fields=["name", "company"]): - if warehouse.company: - warehouse_associcated_with_company = True - - return warehouse_associcated_with_company - -def make_warehouse_nestedset(company=None): - validate_parent_account_for_warehouse(company) - stock_account_group = get_stock_account_group(company.name) - enable_perpetual_inventory = cint(erpnext.is_perpetual_inventory_enabled(company.name)) or 0 - if not stock_account_group and enable_perpetual_inventory: - return - - if company: - warehouse_group = "{0} - {1}".format(_("All Warehouses"), company.abbr) - ignore_mandatory = False - else: - warehouse_group = _("All Warehouses") - ignore_mandatory = True - - if not frappe.db.get_value("Warehouse", warehouse_group): - create_default_warehouse_group(company, stock_account_group, ignore_mandatory) - - set_parent_to_warehouse(warehouse_group, company) - if enable_perpetual_inventory: - set_parent_to_warehouse_account(company) - -def validate_parent_account_for_warehouse(company=None): - if not company: - return - - if cint(erpnext.is_perpetual_inventory_enabled(company.name)): - parent_account = frappe.db.sql("""select name from tabAccount - where account_type='Stock' and company=%s and is_group=1 - and (warehouse is null or warehouse = '')""", company.name) - - if not parent_account: - current_parent_accounts_for_warehouse = frappe.db.sql("""select parent_account from tabAccount - where account_type='Warehouse' and (warehouse is not null or warehouse != '') """) - - if current_parent_accounts_for_warehouse: - frappe.db.set_value("Account", current_parent_accounts_for_warehouse[0][0], "account_type", "Stock") - -def create_default_warehouse_group(company=None, stock_account_group=None, ignore_mandatory=False): - wh = frappe.get_doc({ - "doctype": "Warehouse", - "warehouse_name": _("All Warehouses"), - "is_group": 1, - "company": company.name if company else "", - "parent_warehouse": "" - }) - - if ignore_mandatory: - wh.flags.ignore_mandatory = ignore_mandatory - - wh.insert(ignore_permissions=True) - -def set_parent_to_warehouse(warehouse_group, company=None): - frappe.db.sql(""" update tabWarehouse set parent_warehouse = %s, is_group = 0 - where (is_group = 0 or is_group is null or is_group = '') and ifnull(company, '') = %s - """,(warehouse_group, company.name if company else "")) - - rebuild_tree("Warehouse", "parent_warehouse") - -def set_parent_to_warehouse_account(company): - frappe.db.sql(""" update tabAccount set parent_account = %s - where is_group = 0 and account_type = "Warehouse" - and (warehouse is not null or warehouse != '') and company = %s - """,("{0} - {1}".format(_("All Warehouses"), company.abbr), company.name)) - - rebuild_tree("Account", "parent_account") - -def set_company_to_warehouse(company): - frappe.db.sql("update tabWahouse set company=%s", company) - -def get_stock_account_group(company): - stock_account_group = frappe.db.get_all('Account', filters = {'company': company, 'is_group': 1, - 'account_type': 'Stock', 'root_type': 'Asset'}, limit=1) - - if not stock_account_group: - stock_account_group = frappe.db.get_all('Account', filters = {'company': company, 'is_group': 1, - 'parent_account': '', 'root_type': 'Asset'}, limit=1) - - return stock_account_group[0].name if stock_account_group else None \ No newline at end of file diff --git a/erpnext/patches/v7_0/fix_duplicate_icons.py b/erpnext/patches/v7_0/fix_duplicate_icons.py deleted file mode 100644 index 9f442029b5..0000000000 --- a/erpnext/patches/v7_0/fix_duplicate_icons.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import unicode_literals -import frappe - -from frappe.desk.doctype.desktop_icon.desktop_icon import (sync_desktop_icons, - get_desktop_icons, set_hidden) -from erpnext.patches.v7_0.migrate_schools_to_erpnext import reload_doctypes_for_schools_icons - -def execute(): - '''hide new style icons if old ones are set''' - frappe.reload_doc('desk', 'doctype', 'desktop_icon') - - reload_doctypes_for_schools_icons() - - sync_desktop_icons() - - for user in frappe.get_all('User', filters={'user_type': 'System User'}): - desktop_icons = get_desktop_icons(user.name) - icons_dict = {} - for d in desktop_icons: - if not d.hidden: - icons_dict[d.module_name] = d - - for key in (('Selling', 'Customer'), ('Stock', 'Item'), ('Buying', 'Supplier'), - ('HR', 'Employee'), ('CRM', 'Lead'), ('Support', 'Issue'), ('Projects', 'Project')): - if key[0] in icons_dict and key[1] in icons_dict: - set_hidden(key[1], user.name, 1) - diff --git a/erpnext/patches/v7_0/fix_nonwarehouse_ledger_gl_entries_for_transactions.py b/erpnext/patches/v7_0/fix_nonwarehouse_ledger_gl_entries_for_transactions.py deleted file mode 100644 index 2bc09714d8..0000000000 --- a/erpnext/patches/v7_0/fix_nonwarehouse_ledger_gl_entries_for_transactions.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import print_function, unicode_literals -import frappe, erpnext - -def execute(): - frappe.reload_doctype("Account") - - warehouses = frappe.db.sql("""select name, company from tabAccount - where account_type = 'Stock' and is_group = 0 - and (warehouse is null or warehouse = '')""", as_dict=1) - warehouses = [d.name for d in warehouses if erpnext.is_perpetual_inventory_enabled(d.company)] - - if len(warehouses) > 0: - warehouses = set_warehouse_for_stock_account(warehouses) - if not warehouses: - return - - stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no - from `tabStock Ledger Entry` sle - where sle.warehouse in (%s) and creation > '2016-05-01' - and not exists(select name from `tabGL Entry` - where account=sle.warehouse and voucher_type=sle.voucher_type and voucher_no=sle.voucher_no) - order by sle.posting_date""" % - ', '.join(['%s']*len(warehouses)), tuple(warehouses)) - - rejected = [] - for voucher_type, voucher_no in stock_vouchers: - try: - frappe.db.sql("""delete from `tabGL Entry` - where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no)) - - voucher = frappe.get_doc(voucher_type, voucher_no) - voucher.make_gl_entries() - frappe.db.commit() - except Exception as e: - print(frappe.get_traceback()) - rejected.append([voucher_type, voucher_no]) - frappe.db.rollback() - - print(rejected) - -def set_warehouse_for_stock_account(warehouse_account): - for account in warehouse_account: - if frappe.db.exists('Warehouse', account): - frappe.db.set_value("Account", account, "warehouse", account) - else: - warehouse_account.remove(account) - - return warehouse_account diff --git a/erpnext/patches/v7_0/make_guardian.py b/erpnext/patches/v7_0/make_guardian.py deleted file mode 100644 index 519969b38d..0000000000 --- a/erpnext/patches/v7_0/make_guardian.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.exists("DocType", "Student"): - student_table_cols = frappe.db.get_table_columns("Student") - if "father_name" in student_table_cols: - - # 'Schools' module changed to the 'Education' - # frappe.reload_doc("schools", "doctype", "student") - # frappe.reload_doc("schools", "doctype", "guardian") - # frappe.reload_doc("schools", "doctype", "guardian_interest") - - frappe.reload_doc("education", "doctype", "student") - frappe.reload_doc("education", "doctype", "guardian") - frappe.reload_doc("education", "doctype", "guardian_interest") - frappe.reload_doc("hr", "doctype", "interest") - - fields = ["name", "father_name", "mother_name"] - - if "father_email_id" in student_table_cols: - fields += ["father_email_id", "mother_email_id"] - - students = frappe.get_all("Student", fields) - for stud in students: - if stud.father_name: - make_guardian(stud.father_name, stud.name, stud.father_email_id) - if stud.mother_name: - make_guardian(stud.mother_name, stud.name, stud.mother_email_id) - -def make_guardian(name, student, email=None): - frappe.get_doc({ - 'doctype': 'Guardian', - 'guardian_name': name, - 'email': email, - 'student': student - }).insert() diff --git a/erpnext/patches/v7_0/make_is_group_fieldtype_as_check.py b/erpnext/patches/v7_0/make_is_group_fieldtype_as_check.py deleted file mode 100644 index ba82e869fa..0000000000 --- a/erpnext/patches/v7_0/make_is_group_fieldtype_as_check.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - for doctype in ["Sales Person", "Customer Group", "Item Group", "Territory"]: - - # convert to 1 or 0 - frappe.db.sql("update `tab{doctype}` set is_group = if(is_group='Yes',1,0) " - .format(doctype=doctype)) - - frappe.db.commit() - - # alter fields to int - - frappe.db.sql("alter table `tab{doctype}` change is_group is_group int(1) default '0'" - .format(doctype=doctype)) - - frappe.reload_doctype(doctype) diff --git a/erpnext/patches/v7_0/merge_account_type_stock_and_warehouse_to_stock.py b/erpnext/patches/v7_0/merge_account_type_stock_and_warehouse_to_stock.py deleted file mode 100644 index 02808a742f..0000000000 --- a/erpnext/patches/v7_0/merge_account_type_stock_and_warehouse_to_stock.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("accounts", "doctype", "account") - - frappe.db.sql(""" update tabAccount set account_type = "Stock" - where account_type = "Warehouse" """) - - frappe.db.commit() \ No newline at end of file diff --git a/erpnext/patches/v7_0/migrate_mode_of_payments_v6_to_v7.py b/erpnext/patches/v7_0/migrate_mode_of_payments_v6_to_v7.py deleted file mode 100644 index e0e3f7075a..0000000000 --- a/erpnext/patches/v7_0/migrate_mode_of_payments_v6_to_v7.py +++ /dev/null @@ -1,38 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('accounts', 'doctype', 'sales_invoice_timesheet') - frappe.reload_doc('accounts', 'doctype', 'sales_invoice_payment') - frappe.reload_doc('accounts', 'doctype', 'mode_of_payment') - - count = 0 - for data in frappe.db.sql("""select name, mode_of_payment, cash_bank_account, paid_amount, company - from `tabSales Invoice` si - where si.is_pos = 1 and si.docstatus < 2 - and si.cash_bank_account is not null and si.cash_bank_account != '' - and not exists(select name from `tabSales Invoice Payment` where parent=si.name)""", as_dict=1): - - if not data.mode_of_payment and not frappe.db.exists("Mode of Payment", "Cash"): - mop = frappe.new_doc("Mode of Payment") - mop.mode_of_payment = "Cash" - mop.type = "Cash" - mop.save() - - si_doc = frappe.get_doc('Sales Invoice', data.name) - row = si_doc.append('payments', { - 'mode_of_payment': data.mode_of_payment or 'Cash', - 'account': data.cash_bank_account, - 'type': frappe.db.get_value('Mode of Payment', data.mode_of_payment, 'type') or 'Cash', - 'amount': data.paid_amount - }) - row.db_update() - - si_doc.set_paid_amount() - si_doc.db_set("paid_amount", si_doc.paid_amount, update_modified = False) - si_doc.db_set("base_paid_amount", si_doc.base_paid_amount, update_modified = False) - - count +=1 - - if count % 200 == 0: - frappe.db.commit() \ No newline at end of file diff --git a/erpnext/patches/v7_0/migrate_schools_to_erpnext.py b/erpnext/patches/v7_0/migrate_schools_to_erpnext.py deleted file mode 100644 index b72bc137b6..0000000000 --- a/erpnext/patches/v7_0/migrate_schools_to_erpnext.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import unicode_literals -import frappe, os -from frappe.installer import remove_from_installed_apps - -def execute(): - reload_doctypes_for_schools_icons() - - frappe.reload_doc('website', 'doctype', 'portal_settings') - frappe.reload_doc('website', 'doctype', 'portal_menu_item') - frappe.reload_doc('buying', 'doctype', 'request_for_quotation') - - if 'schools' in frappe.get_installed_apps(): - if not frappe.db.exists('Module Def', 'Schools') and frappe.db.exists('Module Def', 'Academics'): - - # 'Schools' module changed to the 'Education' - # frappe.rename_doc("Module Def", "Academics", "Schools") - - frappe.rename_doc("Module Def", "Academics", "Education") - - remove_from_installed_apps("schools") - -def reload_doctypes_for_schools_icons(): - # 'Schools' module changed to the 'Education' - # base_path = frappe.get_app_path('erpnext', 'schools', 'doctype') - - base_path = frappe.get_app_path('erpnext', 'education', 'doctype') - for doctype in os.listdir(base_path): - if os.path.exists(os.path.join(base_path, doctype, doctype + '.json')) \ - and doctype not in ("fee_component", "assessment", "assessment_result"): - frappe.reload_doc('education', 'doctype', doctype) \ No newline at end of file diff --git a/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py b/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py deleted file mode 100644 index 998c4b674b..0000000000 --- a/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('accounts', 'doctype', 'sales_invoice') - frappe.reload_doc('accounts', 'doctype', 'sales_invoice_payment') - for time_sheet in frappe.db.sql(""" select sales_invoice, name, total_billable_amount from `tabTimesheet` - where sales_invoice is not null and docstatus < 2""", as_dict=True): - if not frappe.db.exists('Sales Invoice', time_sheet.sales_invoice): - continue - si_doc = frappe.get_doc('Sales Invoice', time_sheet.sales_invoice) - ts = si_doc.append('timesheets',{}) - ts.time_sheet = time_sheet.name - ts.billing_amount = time_sheet.total_billable_amount - ts.db_update() - si_doc.calculate_billing_amount_from_timesheet() - si_doc.db_set("total_billing_amount", si_doc.total_billing_amount, update_modified = False) \ No newline at end of file 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 deleted file mode 100644 index 910814fd22..0000000000 --- a/erpnext/patches/v7_0/po_status_issue_for_pr_return.py +++ /dev/null @@ -1,40 +0,0 @@ -# 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 - -def execute(): - parent_list = [] - count = 0 - - 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 - `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', - {'item_code': data.item_code, 'parent': data.purchase_order, 'idx': data.idx}, 'name') - - if name: - frappe.db.set_value('Purchase Receipt Item', data.name, 'purchase_order_item', name, update_modified=False) - parent_list.append(data.parent) - - count +=1 - if count % 200 == 0: - frappe.db.commit() - - if len(parent_list) > 0: - for parent in set(parent_list): - doc = frappe.get_doc('Purchase Receipt', parent) - doc.update_qty(update_modified=False) \ No newline at end of file diff --git a/erpnext/patches/v7_0/re_route.py b/erpnext/patches/v7_0/re_route.py deleted file mode 100644 index 3cec6f39b2..0000000000 --- a/erpnext/patches/v7_0/re_route.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import unicode_literals -from frappe.patches.v7_0.re_route import update_routes - -def execute(): - update_routes(['Item', 'Item Group', 'Sales Partner', 'Job Opening']) \ No newline at end of file diff --git a/erpnext/patches/v7_0/remove_administrator_role_in_doctypes.py b/erpnext/patches/v7_0/remove_administrator_role_in_doctypes.py deleted file mode 100644 index 8c87c4e3d3..0000000000 --- a/erpnext/patches/v7_0/remove_administrator_role_in_doctypes.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.db.sql("""delete from tabDocPerm where role="Administrator" and parent in - ("Payment Gateway", "Payment Gateway Account", "Payment Request", "Academic Term", "Academic Year", "Course", - "Course Schedule", "Examination", "Fee Category", "Fee Structure", "Fees", "Instructor", "Program", "Program Enrollment Tool", - "Room", "Scheduling Tool", "Student", "Student Applicant", "Student Attendance", "Student Group", "Student Group Creation Tool") - """) \ No newline at end of file diff --git a/erpnext/patches/v7_0/remove_doctypes_and_reports.py b/erpnext/patches/v7_0/remove_doctypes_and_reports.py deleted file mode 100644 index 2356e2f6ee..0000000000 --- a/erpnext/patches/v7_0/remove_doctypes_and_reports.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.table_exists("Time Log"): - frappe.db.sql("""delete from `tabDocType` - 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 `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({ - 'doctype': 'Custom Field', - 'label': data.label, - 'dt': 'Timesheet Detail', - 'fieldname': data.fieldname, - 'fieldtype': data.fieldtype or "Data" - }).insert(ignore_permissions=True) - - frappe.db.sql("""delete from `tabCustom Field` where dt = 'Time Log'""") - frappe.reload_doc('projects', 'doctype', 'timesheet') - frappe.reload_doc('projects', 'doctype', 'timesheet_detail') - - report = "Daily Time Log Summary" - if frappe.db.exists("Report", report): - frappe.delete_doc('Report', report) diff --git a/erpnext/patches/v7_0/remove_features_setup.py b/erpnext/patches/v7_0/remove_features_setup.py deleted file mode 100644 index 49393cc248..0000000000 --- a/erpnext/patches/v7_0/remove_features_setup.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import unicode_literals -import frappe - -from erpnext.setup.install import create_compact_item_print_custom_field -from frappe.utils import cint - -def execute(): - frappe.reload_doctype('Stock Settings') - stock_settings = frappe.get_doc('Stock Settings', 'Stock Settings') - stock_settings.show_barcode_field = cint(frappe.db.get_value("Features Setup", None, "fs_item_barcode")) - if not frappe.db.exists("UOM", stock_settings.stock_uom): - stock_settings.stock_uom = None - stock_settings.save() - - create_compact_item_print_custom_field() - - compact_item_print = frappe.db.get_value("Features Setup", None, "compact_item_print") - frappe.db.set_value("Print Settings", None, "compact_item_print", compact_item_print) - - # remove defaults - frappe.db.sql("""delete from tabDefaultValue where defkey in ('fs_item_serial_nos', - 'fs_item_batch_nos', 'fs_brands', 'fs_item_barcode', - 'fs_item_advanced', 'fs_packing_details', 'fs_item_group_in_details', - 'fs_exports', 'fs_imports', 'fs_discounts', 'fs_purchase_discounts', - 'fs_after_sales_installations', 'fs_projects', 'fs_sales_extras', - 'fs_recurring_invoice', 'fs_pos', 'fs_manufacturing', 'fs_quality', - 'fs_page_break', 'fs_more_info', 'fs_pos_view', 'compact_item_print')""") - - frappe.delete_doc('DocType', 'Features Setup') diff --git a/erpnext/patches/v7_0/remove_old_earning_deduction_doctypes.py b/erpnext/patches/v7_0/remove_old_earning_deduction_doctypes.py deleted file mode 100644 index 05a2c49461..0000000000 --- a/erpnext/patches/v7_0/remove_old_earning_deduction_doctypes.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.exists("DocType", "Salary Component"): - for dt in ("Salary Structure Earning", "Salary Structure Deduction", "Salary Slip Earning", - "Salary Slip Deduction", "Earning Type", "Deduction Type"): - frappe.delete_doc("DocType", dt) - - - for d in frappe.db.sql("""select name from `tabCustom Field` - where dt in ('Salary Detail', 'Salary Component')"""): - frappe.get_doc("Custom Field", d[0]).save() \ No newline at end of file diff --git a/erpnext/patches/v7_0/rename_advance_table_fields.py b/erpnext/patches/v7_0/rename_advance_table_fields.py deleted file mode 100644 index 34d81343e2..0000000000 --- a/erpnext/patches/v7_0/rename_advance_table_fields.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import rename_field - -def execute(): - for dt in ("Sales Invoice Advance", "Purchase Invoice Advance"): - frappe.reload_doctype(dt) - - frappe.db.sql("update `tab{0}` set reference_type = 'Journal Entry'".format(dt)) - - if frappe.get_meta(dt).has_field('journal_entry'): - rename_field(dt, "journal_entry", "reference_name") - - if frappe.get_meta(dt).has_field('jv_detail_no'): - rename_field(dt, "jv_detail_no", "reference_row") \ No newline at end of file diff --git a/erpnext/patches/v7_0/rename_examination_to_assessment.py b/erpnext/patches/v7_0/rename_examination_to_assessment.py deleted file mode 100644 index dc248de4fa..0000000000 --- a/erpnext/patches/v7_0/rename_examination_to_assessment.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -from frappe.model.utils.rename_field import rename_field - -def execute(): - if frappe.db.exists("DocType", "Examination"): - frappe.rename_doc("DocType", "Examination", "Assessment") - frappe.rename_doc("DocType", "Examination Result", "Assessment Result") - - # 'Schools' module changed to the 'Education' - # frappe.reload_doc("schools", "doctype", "assessment") - # frappe.reload_doc("schools", "doctype", "assessment_result") - - frappe.reload_doc("education", "doctype", "assessment") - frappe.reload_doc("education", "doctype", "assessment_result") - - rename_field("Assessment", "exam_name", "assessment_name") - rename_field("Assessment", "exam_code", "assessment_code") - - frappe.db.sql("delete from `tabPortal Menu Item` where route = '/examination'") \ No newline at end of file diff --git a/erpnext/patches/v7_0/rename_fee_amount_to_fee_component.py b/erpnext/patches/v7_0/rename_fee_amount_to_fee_component.py deleted file mode 100644 index 5cb6a3b7c4..0000000000 --- a/erpnext/patches/v7_0/rename_fee_amount_to_fee_component.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -from frappe.model.utils.rename_field import rename_field - -def execute(): - if frappe.db.exists("DocType", "Fee Amount"): - frappe.rename_doc("DocType", "Fee Amount", "Fee Component") - for dt in ("Fees", "Fee Structure"): - frappe.reload_doctype(dt) - rename_field(dt, "amount", "components") - - \ No newline at end of file diff --git a/erpnext/patches/v7_0/rename_prevdoc_fields.py b/erpnext/patches/v7_0/rename_prevdoc_fields.py deleted file mode 100644 index ded4ad4aae..0000000000 --- a/erpnext/patches/v7_0/rename_prevdoc_fields.py +++ /dev/null @@ -1,76 +0,0 @@ -from __future__ import unicode_literals -import frappe -import json -from frappe.model.utils.rename_field import update_reports, rename_field, update_property_setters -from frappe.custom.doctype.property_setter.property_setter import make_property_setter - -def execute(): - frappe.reload_doctype('Purchase Order Item') - frappe.reload_doctype('Purchase Receipt Item') - update_po_fields() - update_prop_setters_reports_print_format_for_po() - set_sales_order_field() - rename_pr_fields() - -def update_po_fields(): - for data in frappe.db.sql(""" select prevdoc_docname, prevdoc_detail_docname, name, prevdoc_doctype - from `tabPurchase Order Item` where prevdoc_doctype is not null""", as_dict=True): - if data.prevdoc_doctype == 'Material Request': - frappe.db.set_value("Purchase Order Item", data.name, "material_request", data.prevdoc_docname, update_modified=False) - frappe.db.set_value("Purchase Order Item", data.name, "material_request_item", data.prevdoc_detail_docname, update_modified=False) - elif data.prevdoc_doctype == 'Sales Order': - frappe.db.set_value("Purchase Order Item", data.name, "sales_order", data.prevdoc_docname, update_modified=False) - frappe.db.set_value("Purchase Order Item", data.name, "sales_order_item", data.prevdoc_detail_docname, update_modified=False) - -def get_columns(): - return { - 'prevdoc_docname': 'material_request', - 'prevdoc_detail_docname': 'material_request_item' - } - -def update_prop_setters_reports_print_format_for_po(): - for key, val in get_columns().items(): - update_property_setters('Purchase Order Item', key, val) - update_reports('Purchase Order Item', key, val) - update_print_format_for_po(key, val, 'Purchase Order') - -def update_print_format_for_po(old_fieldname, new_fieldname, doc_type): - column_mapper = get_columns() - - for data in frappe.db.sql(""" select name, format_data from `tabPrint Format` where - format_data like %(old_fieldname)s and doc_type = %(doc_type)s""", - {'old_fieldname': '%%%s%%'%(old_fieldname), 'doc_type': doc_type}, as_dict=True): - - update_print_format_fields(old_fieldname, new_fieldname, data) - -def update_print_format_fields(old_fieldname, new_fieldname, args): - report_dict = json.loads(args.format_data) - update = False - - for col in report_dict: - if col.get('fieldname') and col.get('fieldname') == old_fieldname: - col['fieldname'] = new_fieldname - update = True - - if col.get('visible_columns'): - for key in col.get('visible_columns'): - if key.get('fieldname') == old_fieldname: - key['fieldname'] = new_fieldname - update = True - - if update: - val = json.dumps(report_dict) - frappe.db.sql("""update `tabPrint Format` set `format_data`=%s where name=%s""", (val, args.name)) - -def set_sales_order_field(): - for data in frappe.db.sql("""select doc_type, field_name, property, value, property_type - from `tabProperty Setter` where doc_type = 'Purchase Order Item' - and field_name in('material_request', 'material_request_item')""", as_dict=True): - if data.field_name == 'material_request': - make_property_setter(data.doc_type, 'sales_order', data.property, data.value, data.property_type) - else: - make_property_setter(data.doc_type, 'sales_order_item', data.property, data.value, data.property_type) - -def rename_pr_fields(): - rename_field("Purchase Receipt Item", "prevdoc_docname", "purchase_order") - rename_field("Purchase Receipt Item", "prevdoc_detail_docname", "purchase_order_item") diff --git a/erpnext/patches/v7_0/rename_salary_components.py b/erpnext/patches/v7_0/rename_salary_components.py deleted file mode 100644 index 1693f3bdf1..0000000000 --- a/erpnext/patches/v7_0/rename_salary_components.py +++ /dev/null @@ -1,149 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import update_property_setters - -def execute(): - if not frappe.db.exists("DocType", "Salary Structure Earning"): - return - - frappe.reload_doc("Payroll", "doctype", "salary_detail") - frappe.reload_doc("Payroll", "doctype", "salary_component") - - standard_cols = ["name", "creation", "modified", "owner", "modified_by", "parent", "parenttype", "parentfield", "idx"] - - dt_cols = { - "Salary Structure Deduction": ["d_type", "d_modified_amt", "depend_on_lwp"], - "Salary Structure Earning": ["e_type", "modified_value", "depend_on_lwp"], - "Salary Slip Earning": ["e_type", "e_modified_amount", "e_depends_on_lwp", "e_amount"], - "Salary Slip Deduction": ["d_type", "d_modified_amount", "d_depends_on_lwp", "d_amount"], - } - - earning_type_exists = True if "earning_type" in frappe.db.get_table_columns("Salary Slip Earning") else False - e_type_exists = True if "e_type" in frappe.db.get_table_columns("Salary Slip Earning") else False - - - if e_type_exists and earning_type_exists: - frappe.db.sql("""update `tabSalary Slip Earning` - set e_type = earning_type, e_modified_amount = earning_amount - where e_type is null and earning_type is not null""") - - frappe.db.sql("""update `tabSalary Structure Earning` set e_type = earning_type - where e_type is null and earning_type is not null""") - - frappe.db.sql("""update `tabSalary Slip Deduction` set - d_type = deduction_type, d_modified_amount = deduction_amount - where d_type is null and deduction_type is not null""") - - frappe.db.sql("""update `tabSalary Structure Deduction` set d_type = deduction_type - where d_type is null and deduction_type is not null""") - - if earning_type_exists and not e_type_exists: - for val in dt_cols.values(): - if val[0] == "e_type": - val[0] = "earning_type" - - if val[0] == "d_type": - val[0] = "deduction_type" - - if val[1] == "e_modified_amount": - val[1] ="earning_amount" - - if val[1] == "d_modified_amount": - val[1] ="deduction_amount" - - - - target_cols = standard_cols + ["salary_component", "amount", "depends_on_payment_days", "default_amount"] - target_cols = "`" + "`, `".join(target_cols) + "`" - - for doctype, cols in dt_cols.items(): - source_cols = "`" + "`, `".join(standard_cols + cols) + "`" - if len(cols) == 3: - source_cols += ", 0" - - - frappe.db.sql("""INSERT INTO `tabSalary Detail` ({0}) SELECT {1} FROM `tab{2}`""" - .format(target_cols, source_cols, doctype)) - - - dt_cols_de = { - "Deduction Type": ["deduction_name", "description"], - "Earning Type": ["earning_name", "description"], - } - - standard_cols_de = standard_cols - - - target_cols = standard_cols_de + ["salary_component", "description"] - target_cols = "`" + "`, `".join(target_cols) + "`" - - for doctype, cols in dt_cols_de.items(): - source_cols = "`" + "`, `".join(standard_cols_de + cols) + "`" - try: - frappe.db.sql("""INSERT INTO `tabSalary Component` ({0}) SELECT {1} FROM `tab{2}`""" - .format(target_cols, source_cols, doctype)) - except Exception as e: - if e.args[0]==1062: - pass - - update_customizations() - - for doctype in ["Salary Structure Deduction", "Salary Structure Earning", "Salary Slip Earning", - "Salary Slip Deduction", "Deduction Type", "Earning Type"] : - frappe.delete_doc("DocType", doctype) - - -def update_customizations(): - dt_cols = { - "Salary Structure Deduction": { - "d_type": "salary_component", - "deduction_type": "salary_component", - "d_modified_amt": "amount", - "depend_on_lwp": "depends_on_payment_days" - }, - "Salary Structure Earning": { - "e_type": "salary_component", - "earning_type": "salary_component", - "modified_value": "amount", - "depend_on_lwp": "depends_on_payment_days" - }, - "Salary Slip Earning": { - "e_type": "salary_component", - "earning_type": "salary_component", - "e_modified_amount": "amount", - "e_amount" : "default_amount", - "e_depends_on_lwp": "depends_on_payment_days" - }, - "Salary Slip Deduction": { - "d_type": "salary_component", - "deduction_type": "salary_component", - "d_modified_amount": "amount", - "d_amount" : "default_amount", - "d_depends_on_lwp": "depends_on_payment_days" - } - } - - update_property_setters_and_custom_fields("Salary Detail", dt_cols) - - dt_cols = { - "Earning Type": { - "earning_name": "salary_component" - }, - "Deduction Type": { - "deduction_name": "salary_component" - } - } - - update_property_setters_and_custom_fields("Salary Component", dt_cols) - - - - -def update_property_setters_and_custom_fields(new_dt, dt_cols): - for doctype, cols in dt_cols.items(): - frappe.db.sql("update `tabProperty Setter` set doc_type = %s where doc_type=%s", (new_dt, doctype)) - frappe.db.sql("update `tabCustom Field` set dt = %s where dt=%s", (new_dt, doctype)) - - - for old_fieldname, new_fieldname in cols.items(): - update_property_setters(new_dt, old_fieldname, new_fieldname) diff --git a/erpnext/patches/v7_0/rename_time_sheet_doctype.py b/erpnext/patches/v7_0/rename_time_sheet_doctype.py deleted file mode 100644 index f80a8301d7..0000000000 --- a/erpnext/patches/v7_0/rename_time_sheet_doctype.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.table_exists("Time Sheet") and not frappe.db.table_exists("Timesheet"): - frappe.rename_doc("DocType", "Time Sheet", "Timesheet") - frappe.rename_doc("DocType", "Time Sheet Detail", "Timesheet Detail") - - for doctype in ['Time Sheet', 'Time Sheet Detail']: - frappe.delete_doc('DocType', doctype) - - report = "Daily Time Sheet Summary" - if frappe.db.exists("Report", report): - frappe.delete_doc('Report', report) diff --git a/erpnext/patches/v7_0/repost_bin_qty_and_item_projected_qty.py b/erpnext/patches/v7_0/repost_bin_qty_and_item_projected_qty.py deleted file mode 100644 index a5cf22cf01..0000000000 --- a/erpnext/patches/v7_0/repost_bin_qty_and_item_projected_qty.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - repost_bin_qty() - -def repost_bin_qty(): - for bin in frappe.db.sql(""" select name from `tabBin` - where (actual_qty + ordered_qty + indented_qty + planned_qty - reserved_qty - reserved_qty_for_production - reserved_qty_for_sub_contract) != projected_qty """, as_dict=1): - bin_doc = frappe.get_doc('Bin', bin.name) - bin_doc.set_projected_qty() - bin_doc.db_set("projected_qty", bin_doc.projected_qty, update_modified = False) diff --git a/erpnext/patches/v7_0/repost_gle_for_pi_with_update_stock.py b/erpnext/patches/v7_0/repost_gle_for_pi_with_update_stock.py deleted file mode 100644 index b864e597b8..0000000000 --- a/erpnext/patches/v7_0/repost_gle_for_pi_with_update_stock.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from frappe.utils import cint - -def execute(): - frappe.reload_doctype("Purchase Invoice") - - for pi in frappe.db.sql("""select name from `tabPurchase Invoice` - where company in(select name from tabCompany where enable_perpetual_inventory = 1) and - update_stock=1 and docstatus=1 order by posting_date asc""", as_dict=1): - - frappe.db.sql("""delete from `tabGL Entry` - where voucher_type = 'Purchase Invoice' and voucher_no = %s""", pi.name) - - pi_doc = frappe.get_doc("Purchase Invoice", pi.name) - pi_doc.make_gl_entries() - frappe.db.commit() \ No newline at end of file diff --git a/erpnext/patches/v7_0/repost_gle_for_pos_sales_return.py b/erpnext/patches/v7_0/repost_gle_for_pos_sales_return.py deleted file mode 100644 index 77ecafd6f1..0000000000 --- a/erpnext/patches/v7_0/repost_gle_for_pos_sales_return.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from frappe.utils import cint, flt - -def execute(): - frappe.reload_doctype("Sales Invoice") - frappe.reload_doctype("Sales Invoice Item") - - for si in frappe.get_all("Sales Invoice", fields = ["name"], - filters={"docstatus": 1, "is_pos": 1, "is_return": 1}): - si_doc = frappe.get_doc("Sales Invoice", si.name) - if len(si_doc.payments) > 0: - si_doc.set_paid_amount() - si_doc.flags.ignore_validate_update_after_submit = True - si_doc.save() - if si_doc.grand_total <= si_doc.paid_amount and si_doc.paid_amount < 0: - delete_gle_for_voucher(si_doc.name) - si_doc.run_method("make_gl_entries") - -def delete_gle_for_voucher(voucher_no): - frappe.db.sql("""delete from `tabGL Entry` where voucher_no = %(voucher_no)s""", - {'voucher_no': voucher_no}) diff --git a/erpnext/patches/v7_0/set_base_amount_in_invoice_payment_table.py b/erpnext/patches/v7_0/set_base_amount_in_invoice_payment_table.py deleted file mode 100644 index 5dd61a06cc..0000000000 --- a/erpnext/patches/v7_0/set_base_amount_in_invoice_payment_table.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.utils import flt - -def execute(): - si_list = frappe.db.sql(""" - select distinct parent - from `tabSales Invoice Payment` - where docstatus!=2 and parenttype = 'Sales Invoice' - and amount != 0 and base_amount = 0 - """) - - count = 0 - for d in si_list: - si = frappe.get_doc("Sales Invoice", d[0]) - for p in si.get("payments"): - if p.amount and not p.base_amount: - base_amount = flt(p.amount*si.conversion_rate, si.precision("base_paid_amount")) - frappe.db.set_value("Sales Invoice Payment", p.name, "base_amount", base_amount, update_modified=False) - - count +=1 - - if count % 200 == 0: - frappe.db.commit() diff --git a/erpnext/patches/v7_0/set_is_group_for_warehouse.py b/erpnext/patches/v7_0/set_is_group_for_warehouse.py deleted file mode 100644 index 3e69616b80..0000000000 --- a/erpnext/patches/v7_0/set_is_group_for_warehouse.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("stock", "doctype", "warehouse") - frappe.db.sql("""update tabWarehouse - set is_group = if ((ifnull(is_group, "No") = "Yes" or ifnull(is_group, 0) = 1), 1, 0)""") \ No newline at end of file diff --git a/erpnext/patches/v7_0/set_material_request_type_in_item.py b/erpnext/patches/v7_0/set_material_request_type_in_item.py deleted file mode 100644 index 5fb14adbc8..0000000000 --- a/erpnext/patches/v7_0/set_material_request_type_in_item.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Item") - if "default_bom" in frappe.db.get_table_columns("Item"): - frappe.db.sql("""update `tabItem` - set default_material_request_type = ( - case - when (default_bom is not null and default_bom != '') - then 'Manufacture' - else 'Purchase' - end )""") - - else: - frappe.db.sql("update tabItem set default_material_request_type='Purchase'") \ No newline at end of file diff --git a/erpnext/patches/v7_0/set_naming_series_for_timesheet.py b/erpnext/patches/v7_0/set_naming_series_for_timesheet.py deleted file mode 100644 index d4d1a69d21..0000000000 --- a/erpnext/patches/v7_0/set_naming_series_for_timesheet.py +++ /dev/null @@ -1,15 +0,0 @@ -# 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.custom.doctype.property_setter.property_setter import make_property_setter - -def execute(): - frappe.reload_doc('projects', 'doctype', 'timesheet') - frappe.reload_doc('projects', 'doctype', 'timesheet_detail') - frappe.reload_doc('accounts', 'doctype', 'sales_invoice_timesheet') - - make_property_setter('Timesheet', "naming_series", "options", 'TS-', "Text") - make_property_setter('Timesheet', "naming_series", "default", 'TS-', "Text") \ No newline at end of file diff --git a/erpnext/patches/v7_0/set_party_name_in_payment_entry.py b/erpnext/patches/v7_0/set_party_name_in_payment_entry.py deleted file mode 100644 index bbdcf5cf3c..0000000000 --- a/erpnext/patches/v7_0/set_party_name_in_payment_entry.py +++ /dev/null @@ -1,20 +0,0 @@ -# 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 - -def execute(): - customers = frappe._dict(frappe.db.sql("select name, customer_name from tabCustomer")) - suppliers = frappe._dict(frappe.db.sql("select name, supplier_name from tabSupplier")) - - frappe.reload_doc('accounts', 'doctype', 'payment_entry') - - pe_list = frappe.db.sql("""select name, party_type, party from `tabPayment Entry` - where party is not null and party != ''""", as_dict=1) - for pe in pe_list: - party_name = customers.get(pe.party) if pe.party_type=="Customer" else suppliers.get(pe.party) - - frappe.db.set_value("Payment Entry", pe.name, "party_name", party_name, update_modified=False) - diff --git a/erpnext/patches/v7_0/set_portal_settings.py b/erpnext/patches/v7_0/set_portal_settings.py deleted file mode 100644 index 5259d4fbd4..0000000000 --- a/erpnext/patches/v7_0/set_portal_settings.py +++ /dev/null @@ -1,28 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doctype('Role') - for dt in ("assessment", "course", "fees"): - # 'Schools' module changed to the 'Education' - # frappe.reload_doc("schools", "doctype", dt) - frappe.reload_doc("education", "doctype", dt) - - for dt in ("domain", "has_domain", "domain_settings"): - frappe.reload_doc("core", "doctype", dt) - - frappe.reload_doc('website', 'doctype', 'portal_menu_item') - - frappe.get_doc('Portal Settings').sync_menu() - - if 'schools' in frappe.get_installed_apps(): - domain = frappe.get_doc('Domain', 'Education') - domain.setup_domain() - else: - domain = frappe.get_doc('Domain', 'Manufacturing') - domain.setup_data() - domain.setup_sidebar_items() diff --git a/erpnext/patches/v7_0/setup_account_table_for_expense_claim_type_if_exists.py b/erpnext/patches/v7_0/setup_account_table_for_expense_claim_type_if_exists.py deleted file mode 100644 index c5657079b3..0000000000 --- a/erpnext/patches/v7_0/setup_account_table_for_expense_claim_type_if_exists.py +++ /dev/null @@ -1,20 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("hr", "doctype", "expense_claim_type") - frappe.reload_doc("hr", "doctype", "expense_claim_account") - - if not frappe.db.has_column('Expense Claim Type', 'default_account'): - return - - for expense_claim_type in frappe.get_all("Expense Claim Type", fields=["name", "default_account"]): - if expense_claim_type.default_account \ - and frappe.db.exists("Account", expense_claim_type.default_account): - doc = frappe.get_doc("Expense Claim Type", expense_claim_type.name) - doc.append("accounts", { - "company": frappe.db.get_value("Account", expense_claim_type.default_account, "company"), - "default_account": expense_claim_type.default_account, - }) - doc.flags.ignore_mandatory = True - doc.save(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/patches/v7_0/system_settings_setup_complete.py b/erpnext/patches/v7_0/system_settings_setup_complete.py deleted file mode 100644 index 0feeee981e..0000000000 --- a/erpnext/patches/v7_0/system_settings_setup_complete.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype('System Settings') - companies = frappe.db.sql("""select name, country - from tabCompany order by creation asc""", as_dict=True) - if companies: - frappe.db.set_value('System Settings', 'System Settings', 'setup_complete', 1) - - for company in companies: - if company.country: - frappe.db.set_value('System Settings', 'System Settings', 'country', company.country) - break - - diff --git a/erpnext/patches/v7_0/update_autoname_field.py b/erpnext/patches/v7_0/update_autoname_field.py deleted file mode 100644 index bfa9b281df..0000000000 --- a/erpnext/patches/v7_0/update_autoname_field.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - doctypes = frappe.db.sql(""" select name, autoname from `tabDocType` - where autoname like 'field:%' and allow_rename = 1""", as_dict=1) - - for doctype in doctypes: - fieldname = doctype.autoname.split(":")[1] - if fieldname: - frappe.db.sql(""" update `tab%s` set %s = name """%(doctype.name, fieldname)) \ No newline at end of file diff --git a/erpnext/patches/v7_0/update_change_amount_account.py b/erpnext/patches/v7_0/update_change_amount_account.py deleted file mode 100644 index 1741095ea9..0000000000 --- a/erpnext/patches/v7_0/update_change_amount_account.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import unicode_literals -import frappe -from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account - -def execute(): - frappe.reload_doc('accounts', 'doctype', 'sales_invoice') - - for company in frappe.db.sql("""select company from `tabSales Invoice` - where change_amount <> 0 and account_for_change_amount is null group by company""", as_list = 1): - cash_account = get_default_bank_cash_account(company[0], 'Cash').get('account') - if not cash_account: - bank_account = get_default_bank_cash_account(company[0], 'Bank').get('account') - cash_account = bank_account - - if cash_account: - frappe.db.sql("""update `tabSales Invoice` - set account_for_change_amount = %(cash_account)s where change_amount <> 0 - and company = %(company)s and account_for_change_amount is null""", - {'cash_account': cash_account, 'company': company[0]}) diff --git a/erpnext/patches/v7_0/update_conversion_factor_in_supplier_quotation_item.py b/erpnext/patches/v7_0/update_conversion_factor_in_supplier_quotation_item.py deleted file mode 100644 index 24da4b1aeb..0000000000 --- a/erpnext/patches/v7_0/update_conversion_factor_in_supplier_quotation_item.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('buying', 'doctype', 'supplier_quotation_item') - - frappe.db.sql("""update - `tabSupplier Quotation Item` as sqi_t, - (select sqi.item_code as item_code, sqi.uom as uom, ucd.conversion_factor as conversion_factor - from `tabSupplier Quotation Item` sqi left join `tabUOM Conversion Detail` ucd - on ucd.uom = sqi.uom and sqi.item_code = ucd.parent) as conversion_data, - `tabItem` as item - set - sqi_t.conversion_factor= ifnull(conversion_data.conversion_factor, 1), - sqi_t.stock_qty = (ifnull(conversion_data.conversion_factor, 1) * sqi_t.qty), - sqi_t.stock_uom = item.stock_uom - where - sqi_t.item_code = conversion_data.item_code and - sqi_t.uom = conversion_data.uom and sqi_t.item_code = item.name""") \ No newline at end of file diff --git a/erpnext/patches/v7_0/update_home_page.py b/erpnext/patches/v7_0/update_home_page.py deleted file mode 100644 index 909825c572..0000000000 --- a/erpnext/patches/v7_0/update_home_page.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import unicode_literals -import frappe -import erpnext - -def execute(): - frappe.reload_doc('portal', 'doctype', 'homepage_featured_product') - frappe.reload_doc('portal', 'doctype', 'homepage') - frappe.reload_doc('portal', 'doctype', 'products_settings') - frappe.reload_doctype('Item') - frappe.reload_doctype('Item Group') - - website_settings = frappe.get_doc('Website Settings', 'Website Settings') - if frappe.db.exists('Web Page', website_settings.home_page): - header = frappe.db.get_value('Web Page', website_settings.home_page, 'header') - if header and header.startswith("
"): - homepage = frappe.get_doc('Homepage', 'Homepage') - homepage.company = erpnext.get_default_company() or frappe.get_all("Company")[0].name - if '

' in header: - homepage.tag_line = header.split('

')[1].split('

')[0] or 'Default Website' - else: - homepage.tag_line = 'Default Website' - homepage.setup_items() - homepage.save() - - website_settings.home_page = 'home' - website_settings.save() - diff --git a/erpnext/patches/v7_0/update_maintenance_module_in_doctype.py b/erpnext/patches/v7_0/update_maintenance_module_in_doctype.py deleted file mode 100644 index 4c0c6a9313..0000000000 --- a/erpnext/patches/v7_0/update_maintenance_module_in_doctype.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.db.set_value("DocType", "Maintenance Schedule", "module", "Maintenance") - frappe.db.set_value("DocType", "Maintenance Schedule Detail", "module", "Maintenance") - frappe.db.set_value("DocType", "Maintenance Schedule Item", "module", "Maintenance") - frappe.db.set_value("DocType", "Maintenance Visit", "module", "Maintenance") - frappe.db.set_value("DocType", "Maintenance Visit Purpose", "module", "Maintenance") \ No newline at end of file diff --git a/erpnext/patches/v7_0/update_mins_to_first_response.py b/erpnext/patches/v7_0/update_mins_to_first_response.py deleted file mode 100644 index 16681357e6..0000000000 --- a/erpnext/patches/v7_0/update_mins_to_first_response.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import unicode_literals -import frappe - -from frappe.core.doctype.communication.communication import update_mins_to_first_communication - -def execute(): - frappe.reload_doctype('Issue') - frappe.reload_doctype('Opportunity') - - for doctype in ('Issue', 'Opportunity'): - frappe.db.sql('update tab{0} set mins_to_first_response=0'.format(doctype)) - for parent in frappe.get_all(doctype, order_by='creation desc', limit=500): - parent_doc = frappe.get_doc(doctype, parent.name) - for communication in frappe.get_all('Communication', - filters={'reference_doctype': doctype, 'reference_name': parent.name, - 'communication_medium': 'Email'}, - order_by = 'creation asc', limit=2): - - communication_doc = frappe.get_doc('Communication', communication.name) - - update_mins_to_first_communication(parent_doc, communication_doc) - - if parent_doc.mins_to_first_response: - continue \ No newline at end of file diff --git a/erpnext/patches/v7_0/update_missing_employee_in_timesheet.py b/erpnext/patches/v7_0/update_missing_employee_in_timesheet.py deleted file mode 100644 index 54d492b265..0000000000 --- a/erpnext/patches/v7_0/update_missing_employee_in_timesheet.py +++ /dev/null @@ -1,20 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.table_exists("Time Log") and "employee" in frappe.db.get_table_columns("Time Log"): - timesheet = frappe.db.sql("""select tl.employee as employee, ts.name as name, - tl.modified as modified, tl.modified_by as modified_by, tl.creation as creation, tl.owner as owner - from - `tabTimesheet` ts, `tabTimesheet Detail` tsd, `tabTime Log` tl - where - tsd.parent = ts.name and tl.from_time = tsd.from_time and tl.to_time = tsd.to_time - and tl.hours = tsd.hours and tl.billing_rate = tsd.billing_rate and tsd.idx=1 - and tl.docstatus < 2 and (ts.employee = '' or ts.employee is null)""", as_dict=1) - - for data in timesheet: - ts_doc = frappe.get_doc('Timesheet', data.name) - if len(ts_doc.time_logs) == 1: - frappe.db.sql(""" update `tabTimesheet` set creation = %(creation)s, - owner = %(owner)s, modified = %(modified)s, modified_by = %(modified_by)s, - employee = %(employee)s where name = %(name)s""", data) diff --git a/erpnext/patches/v7_0/update_mode_of_payment_type.py b/erpnext/patches/v7_0/update_mode_of_payment_type.py deleted file mode 100644 index 9292a1be1b..0000000000 --- a/erpnext/patches/v7_0/update_mode_of_payment_type.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.utils import flt - -def execute(): - frappe.reload_doc('accounts', 'doctype', 'mode_of_payment') - - frappe.db.sql(""" update `tabMode of Payment` set type = 'Cash' where (type is null or type = '') and name = 'Cash'""") - - for data in frappe.db.sql("""select name from `tabSales Invoice` where is_pos=1 and docstatus<2 and - (ifnull(paid_amount, 0) - ifnull(change_amount, 0)) > ifnull(grand_total, 0) and modified > '2016-05-01'""", as_dict=1): - if data.name: - si_doc = frappe.get_doc("Sales Invoice", data.name) - remove_payment = [] - mode_of_payment = [d.mode_of_payment for d in si_doc.payments if flt(d.amount) > 0] - if mode_of_payment != set(mode_of_payment): - for payment_data in si_doc.payments: - if payment_data.idx != 1 and payment_data.amount == si_doc.grand_total: - remove_payment.append(payment_data) - frappe.db.sql(""" delete from `tabSales Invoice Payment` - where name = %(name)s""", {'name': payment_data.name}) - - if len(remove_payment) > 0: - for d in remove_payment: - si_doc.remove(d) - - si_doc.set_paid_amount() - si_doc.db_set("paid_amount", si_doc.paid_amount, update_modified = False) - si_doc.db_set("base_paid_amount", si_doc.base_paid_amount, update_modified = False) \ No newline at end of file diff --git a/erpnext/patches/v7_0/update_party_status.py b/erpnext/patches/v7_0/update_party_status.py deleted file mode 100644 index 0c6b4ea598..0000000000 --- a/erpnext/patches/v7_0/update_party_status.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - return - # for party_type in ('Customer', 'Supplier'): - # frappe.reload_doctype(party_type) - # - # # set all as default status - # frappe.db.sql('update `tab{0}` set status=%s'.format(party_type), default_status[party_type]) - # - # for doctype in status_depends_on[party_type]: - # filters = get_filters_for(doctype) - # parties = frappe.get_all(doctype, fields="{0} as party".format(party_type.lower()), - # filters=filters, limit_page_length=1) - # - # parties = filter(None, [p.party for p in parties]) - # - # if parties: - # frappe.db.sql('update `tab{0}` set status="Open" where name in ({1})'.format(party_type, - # ', '.join(len(parties) * ['%s'])), parties) \ No newline at end of file diff --git a/erpnext/patches/v7_0/update_prevdoc_values_for_supplier_quotation_item.py b/erpnext/patches/v7_0/update_prevdoc_values_for_supplier_quotation_item.py deleted file mode 100644 index e90de50c1e..0000000000 --- a/erpnext/patches/v7_0/update_prevdoc_values_for_supplier_quotation_item.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype('Supplier Quotation Item') - for data in frappe.db.sql(""" select prevdoc_docname, prevdoc_detail_docname, name - from `tabSupplier Quotation Item` where prevdoc_docname is not null""", as_dict=True): - frappe.db.set_value("Supplier Quotation Item", data.name, "material_request", data.prevdoc_docname) - frappe.db.set_value("Supplier Quotation Item", data.name, "material_request_item", data.prevdoc_detail_docname) \ No newline at end of file diff --git a/erpnext/patches/v7_0/update_project_in_gl_entry.py b/erpnext/patches/v7_0/update_project_in_gl_entry.py deleted file mode 100644 index d99e9a41e3..0000000000 --- a/erpnext/patches/v7_0/update_project_in_gl_entry.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("GL Entry") - - for doctype in ("Delivery Note", "Sales Invoice", "Stock Entry"): - frappe.db.sql(""" - update `tabGL Entry` gle, `tab{0}` dt - set gle.project = dt.project - where gle.voucher_type=%s and gle.voucher_no = dt.name - and ifnull(gle.cost_center, '') != '' and ifnull(dt.project, '') != '' - """.format(doctype), doctype) - - for doctype in ("Purchase Receipt", "Purchase Invoice"): - frappe.db.sql(""" - update `tabGL Entry` gle, `tab{0} Item` dt - set gle.project = dt.project - where gle.voucher_type=%s and gle.voucher_no = dt.parent and gle.cost_center=dt.cost_center - and ifnull(gle.cost_center, '') != '' and ifnull(dt.project, '') != '' - """.format(doctype), doctype) \ No newline at end of file diff --git a/erpnext/patches/v7_0/update_refdoc_in_landed_cost_voucher.py b/erpnext/patches/v7_0/update_refdoc_in_landed_cost_voucher.py deleted file mode 100644 index 2d562bb40e..0000000000 --- a/erpnext/patches/v7_0/update_refdoc_in_landed_cost_voucher.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if "purchase_receipt" not in frappe.db.get_table_columns("Landed Cost Purchase Receipt"): - return - - frappe.reload_doctype("Landed Cost Purchase Receipt") - - frappe.db.sql(""" - update `tabLanded Cost Purchase Receipt` - set receipt_document_type = 'Purchase Receipt', receipt_document = purchase_receipt - where (receipt_document is null or receipt_document = '') - and (purchase_receipt is not null and purchase_receipt != '') - """) \ No newline at end of file diff --git a/erpnext/patches/v7_0/update_status_for_timesheet.py b/erpnext/patches/v7_0/update_status_for_timesheet.py deleted file mode 100644 index 117c40c59f..0000000000 --- a/erpnext/patches/v7_0/update_status_for_timesheet.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.db.sql("""update - `tabTimesheet` as ts, - ( - select min(from_time)as from_time, max(to_time) as to_time, parent from `tabTimesheet Detail` group by parent - ) as tsd - set ts.status = 'Submitted', ts.start_date = tsd.from_time, ts.end_date = tsd.to_time - where tsd.parent = ts.name and ts.status = 'Draft' and ts.docstatus =1""") \ No newline at end of file diff --git a/erpnext/patches/v7_0/update_status_of_po_so.py b/erpnext/patches/v7_0/update_status_of_po_so.py deleted file mode 100644 index d630e8f0f2..0000000000 --- a/erpnext/patches/v7_0/update_status_of_po_so.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from frappe.utils import cint, flt - -def execute(): - update_po_per_received_per_billed() - update_so_per_delivered_per_billed() - update_status() - -def update_po_per_received_per_billed(): - frappe.db.sql(""" - update - `tabPurchase Order` - set - `tabPurchase Order`.per_received = round((select sum(if(qty > ifnull(received_qty, 0), - ifnull(received_qty, 0), qty)) / sum(qty) *100 from `tabPurchase Order Item` - where parent = `tabPurchase Order`.name), 2), - `tabPurchase Order`.per_billed = ifnull(round((select sum( if(amount > ifnull(billed_amt, 0), - ifnull(billed_amt, 0), amount)) / sum(amount) *100 from `tabPurchase Order Item` - where parent = `tabPurchase Order`.name), 2), 0) - where - net_total > 0 - """) - -def update_so_per_delivered_per_billed(): - frappe.db.sql(""" - update - `tabSales Order` - set - `tabSales Order`.per_delivered = round((select sum( if(qty > ifnull(delivered_qty, 0), - ifnull(delivered_qty, 0), qty)) / sum(qty) *100 from `tabSales Order Item` - where parent = `tabSales Order`.name), 2), - `tabSales Order`.per_billed = ifnull(round((select sum( if(amount > ifnull(billed_amt, 0), - ifnull(billed_amt, 0), amount)) / sum(amount) *100 from `tabSales Order Item` - where parent = `tabSales Order`.name), 2), 0) - where - net_total > 0 - """) - -def update_status(): - frappe.db.sql(""" - update - `tabSales Order` - set status = (Case when status = 'Closed' then 'Closed' - When per_delivered < 100 and per_billed < 100 and docstatus = 1 then 'To Deliver and Bill' - when per_delivered = 100 and per_billed < 100 and docstatus = 1 then 'To Bill' - when per_delivered < 100 and per_billed = 100 and docstatus = 1 then 'To Deliver' - when per_delivered = 100 and per_billed = 100 and docstatus = 1 then 'Completed' - when order_type = 'Maintenance' and per_billed = 100 and docstatus = 1 then 'Completed' - when docstatus = 2 then 'Cancelled' - else 'Draft' - End)""") - - frappe.db.sql(""" - update - `tabPurchase Order` - set status = (Case when status = 'Closed' then 'Closed' - when status = 'Delivered' then 'Delivered' - When per_received < 100 and per_billed < 100 and docstatus = 1 then 'To Receive and Bill' - when per_received = 100 and per_billed < 100 and docstatus = 1 then 'To Bill' - when per_received < 100 and per_billed = 100 and docstatus = 1 then 'To Receive' - when per_received = 100 and per_billed = 100 and docstatus = 1 then 'Completed' - when docstatus = 2 then 'Cancelled' - else 'Draft' - End)""") \ No newline at end of file diff --git a/erpnext/patches/v7_0/update_status_of_zero_amount_sales_order.py b/erpnext/patches/v7_0/update_status_of_zero_amount_sales_order.py deleted file mode 100644 index 9b2b24785a..0000000000 --- a/erpnext/patches/v7_0/update_status_of_zero_amount_sales_order.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - for data in frappe.get_all('Sales Order', fields = ["name"], filters = [["docstatus", "=", "1"], ["grand_total", "=", "0"]]): - sales_order = frappe.get_doc('Sales Order', data.name) - sales_order.set_status(update=True, update_modified = False) \ No newline at end of file diff --git a/erpnext/patches/v7_0/update_timesheet_communications.py b/erpnext/patches/v7_0/update_timesheet_communications.py deleted file mode 100644 index 203471ea8f..0000000000 --- a/erpnext/patches/v7_0/update_timesheet_communications.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.table_exists("Time Log"): - timesheet = frappe.db.sql("""SELECT ts.name AS name, tl.name AS timelogname, - tl.modified AS modified, tl.modified_by AS modified_by, tl.creation AS creation, tl.owner AS owner - FROM - `tabTimesheet` ts, `tabTimesheet Detail` tsd, `tabTime Log` tl - WHERE - tsd.parent = ts.name AND tl.from_time = tsd.from_time AND tl.to_time = tsd.to_time - AND tl.hours = tsd.hours AND tl.billing_rate = tsd.billing_rate AND tsd.idx=1 - AND tl.docstatus < 2""", as_dict=1) - - for data in timesheet: - frappe.db.sql(""" update `tabTimesheet` set creation = %(creation)s, - owner = %(owner)s, modified = %(modified)s, modified_by = %(modified_by)s - where name = %(name)s""", data) - - frappe.db.sql(""" - update - tabCommunication - set - reference_doctype = "Timesheet", reference_name = %(timesheet)s - where - reference_doctype = "Time Log" and reference_name = %(timelog)s - """, {'timesheet': data.name, 'timelog': data.timelogname}, auto_commit=1) diff --git a/erpnext/patches/v7_1/__init__.py b/erpnext/patches/v7_1/__init__.py deleted file mode 100644 index 519ff49eac..0000000000 --- a/erpnext/patches/v7_1/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals \ No newline at end of file diff --git a/erpnext/patches/v7_1/add_account_user_role_for_timesheet.py b/erpnext/patches/v7_1/add_account_user_role_for_timesheet.py deleted file mode 100644 index 7372b0cc5f..0000000000 --- a/erpnext/patches/v7_1/add_account_user_role_for_timesheet.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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 - -def execute(): - if not frappe.db.get_value('DocPerm', {'parent': 'Timesheet', 'role': 'Accounts User', 'permlevel': 1}): - doc = frappe.get_doc('DocType', 'Timesheet') - doc.append('permissions', { - 'role': "Accounts User", - 'permlevel': 0, - 'read': 1, - 'write': 1, - 'create': 1, - 'delete': 1, - 'submit': 1, - 'cancel': 1, - 'amend': 1, - 'report': 1, - 'email': 1 - }) - - doc.append('permissions', { - 'role': "Accounts User", - 'permlevel': 1, - 'read': 1, - 'write': 1 - }) - - doc.save(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/patches/v7_1/add_field_for_task_dependent.py b/erpnext/patches/v7_1/add_field_for_task_dependent.py deleted file mode 100644 index 65b1c74e87..0000000000 --- a/erpnext/patches/v7_1/add_field_for_task_dependent.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype('Task') - for t in frappe.get_all('Task', fields=['name']): - task = frappe.get_doc('Task', t.name) - task.update_depends_on() - if task.depends_on_tasks: - task.db_set('depends_on_tasks', task.depends_on_tasks, update_modified=False) diff --git a/erpnext/patches/v7_1/fix_link_for_customer_from_lead.py b/erpnext/patches/v7_1/fix_link_for_customer_from_lead.py deleted file mode 100644 index 33f809fe37..0000000000 --- a/erpnext/patches/v7_1/fix_link_for_customer_from_lead.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - for c in frappe.db.sql('select name from tabCustomer where ifnull(lead_name,"")!=""'): - customer = frappe.get_doc('Customer', c[0]) - customer.update_lead_status() \ No newline at end of file diff --git a/erpnext/patches/v7_1/move_sales_invoice_from_parent_to_child_timesheet.py b/erpnext/patches/v7_1/move_sales_invoice_from_parent_to_child_timesheet.py deleted file mode 100644 index d1ec7c697e..0000000000 --- a/erpnext/patches/v7_1/move_sales_invoice_from_parent_to_child_timesheet.py +++ /dev/null @@ -1,20 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('projects', 'doctype', 'timesheet_detail') - frappe.reload_doc('accounts', 'doctype', 'sales_invoice_timesheet') - - frappe.db.sql(""" update - `tabTimesheet` as ts, - (select - sum(billing_amount) as billing_amount, sum(billing_hours) as billing_hours, time_sheet - from `tabSales Invoice Timesheet` where docstatus = 1 group by time_sheet - ) as sit - set - ts.total_billed_amount = sit.billing_amount, ts.total_billed_hours = sit.billing_hours, - ts.per_billed = ((sit.billing_amount * 100)/ts.total_billable_amount) - where ts.name = sit.time_sheet and ts.docstatus = 1""") - - frappe.db.sql(""" update `tabTimesheet Detail` tsd, `tabTimesheet` ts set tsd.sales_invoice = ts.sales_invoice - where tsd.parent = ts.name and ts.sales_invoice is not null""") \ No newline at end of file diff --git a/erpnext/patches/v7_1/rename_field_timesheet.py b/erpnext/patches/v7_1/rename_field_timesheet.py deleted file mode 100644 index 3690a2e79d..0000000000 --- a/erpnext/patches/v7_1/rename_field_timesheet.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import rename_field - -def execute(): - doctype = 'Timesheet' - fields_dict = {'total_billing_amount': 'total_billable_amount', 'total_billing_hours': 'total_billable_hours'} - - for old_fieldname, new_fieldname in fields_dict.items(): - if old_fieldname in frappe.db.get_table_columns(doctype): - rename_field(doctype, old_fieldname, new_fieldname) diff --git a/erpnext/patches/v7_1/rename_quality_inspection_field.py b/erpnext/patches/v7_1/rename_quality_inspection_field.py deleted file mode 100644 index 3b5a7d95eb..0000000000 --- a/erpnext/patches/v7_1/rename_quality_inspection_field.py +++ /dev/null @@ -1,38 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import * - -def execute(): - for doctype in ("Purchase Receipt Item", "Delivery Note Item"): - frappe.reload_doctype(doctype) - - table_columns = frappe.db.get_table_columns(doctype) - if "qa_no" in table_columns: - rename_field(doctype, "qa_no", "quality_inspection") - - frappe.reload_doctype("Item") - rename_field("Item", "inspection_required", "inspection_required_before_purchase") - - frappe.reload_doc('stock', 'doctype', 'quality_inspection') - frappe.db.sql(""" - update - `tabQuality Inspection` - set - reference_type = 'Purchase Receipt', reference_name = purchase_receipt_no - where - ifnull(purchase_receipt_no, '') != '' and inspection_type = 'Incoming' - """) - - frappe.db.sql(""" - update - `tabQuality Inspection` - set - reference_type = 'Delivery Note', reference_name = delivery_note_no - where - ifnull(delivery_note_no, '') != '' and inspection_type = 'Outgoing' - """) - - for old_fieldname in ["purchase_receipt_no", "delivery_note_no"]: - update_reports("Quality Inspection", old_fieldname, "reference_name") - update_users_report_view_settings("Quality Inspection", old_fieldname, "reference_name") - update_property_setters("Quality Inspection", old_fieldname, "reference_name") diff --git a/erpnext/patches/v7_1/repost_stock_for_deleted_bins_for_merging_items.py b/erpnext/patches/v7_1/repost_stock_for_deleted_bins_for_merging_items.py deleted file mode 100644 index aca21085cc..0000000000 --- a/erpnext/patches/v7_1/repost_stock_for_deleted_bins_for_merging_items.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import unicode_literals -import frappe -from erpnext.stock.stock_balance import repost_stock - -def execute(): - frappe.reload_doc('manufacturing', 'doctype', 'work_order_item') - frappe.reload_doc('manufacturing', 'doctype', 'work_order') - - modified_items = frappe.db.sql_list(""" - select name from `tabItem` - where is_stock_item=1 and modified >= '2016-10-31' - """) - - if not modified_items: - return - - item_warehouses_with_transactions = [] - transactions = ("Sales Order Item", "Material Request Item", "Purchase Order Item", - "Stock Ledger Entry", "Packed Item") - - for doctype in transactions: - item_warehouses_with_transactions += list(frappe.db.sql(""" - select distinct item_code, warehouse - from `tab{0}` where docstatus=1 and item_code in ({1})""" - .format(doctype, ', '.join(['%s']*len(modified_items))), tuple(modified_items))) - - item_warehouses_with_transactions += list(frappe.db.sql(""" - select distinct production_item, fg_warehouse - from `tabWork Order` where docstatus=1 and production_item in ({0})""" - .format(', '.join(['%s']*len(modified_items))), tuple(modified_items))) - - item_warehouses_with_transactions += list(frappe.db.sql(""" - select distinct pr_item.item_code, pr_item.source_warehouse - from `tabWork Order` pr, `tabWork Order Item` pr_item - where pr_item.parent and pr.name and pr.docstatus=1 and pr_item.item_code in ({0})""" - .format(', '.join(['%s']*len(modified_items))), tuple(modified_items))) - - item_warehouses_with_bin = list(frappe.db.sql("select distinct item_code, warehouse from `tabBin`")) - - item_warehouses_with_missing_bin = list( - set(item_warehouses_with_transactions) - set(item_warehouses_with_bin)) - - for item_code, warehouse in item_warehouses_with_missing_bin: - repost_stock(item_code, warehouse) diff --git a/erpnext/patches/v7_1/save_stock_settings.py b/erpnext/patches/v7_1/save_stock_settings.py deleted file mode 100644 index d3f0263c10..0000000000 --- a/erpnext/patches/v7_1/save_stock_settings.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - stock_settings = frappe.get_doc('Stock Settings') - - if stock_settings.default_warehouse \ - and not frappe.db.exists("Warehouse", stock_settings.default_warehouse): - stock_settings.default_warehouse = None - - if stock_settings.stock_uom and not frappe.db.exists("UOM", stock_settings.stock_uom): - stock_settings.stock_uom = None - - stock_settings.flags.ignore_mandatory = True - stock_settings.save() diff --git a/erpnext/patches/v7_1/set_budget_against_as_cost_center.py b/erpnext/patches/v7_1/set_budget_against_as_cost_center.py deleted file mode 100644 index dd9a432cf0..0000000000 --- a/erpnext/patches/v7_1/set_budget_against_as_cost_center.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("accounts", "doctype", "budget") - frappe.db.sql(""" - update - `tabBudget` - set - budget_against = 'Cost Center' - """) diff --git a/erpnext/patches/v7_1/set_currency_exchange_date.py b/erpnext/patches/v7_1/set_currency_exchange_date.py deleted file mode 100644 index 2a2d420f21..0000000000 --- a/erpnext/patches/v7_1/set_currency_exchange_date.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Currency Exchange") - frappe.db.sql(""" - update `tabCurrency Exchange` - set `date` = '2010-01-01' - where date is null or date = '0000-00-00' - """) \ No newline at end of file diff --git a/erpnext/patches/v7_1/set_prefered_contact_email.py b/erpnext/patches/v7_1/set_prefered_contact_email.py deleted file mode 100644 index 3b68e22269..0000000000 --- a/erpnext/patches/v7_1/set_prefered_contact_email.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype('User') - for d in frappe.get_all("Employee"): - employee = frappe.get_doc("Employee", d.name) - if employee.company_email: - employee.prefered_contact_email = "Company Email" - employee.prefered_email = employee.company_email - elif employee.personal_email: - employee.prefered_contact_email = "Personal Email" - employee.prefered_email = employee.personal_email - elif employee.user_id: - employee.prefered_contact_email = "User ID" - employee.prefered_email = employee.user_id - employee.db_update() diff --git a/erpnext/patches/v7_1/set_sales_person_status.py b/erpnext/patches/v7_1/set_sales_person_status.py deleted file mode 100644 index 929beac27f..0000000000 --- a/erpnext/patches/v7_1/set_sales_person_status.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('setup','doctype','sales_person') - frappe.db.sql("""update `tabSales Person` set enabled=1 - where (employee is null or employee = '' - or employee IN (select employee from tabEmployee where status != "Left"))""") diff --git a/erpnext/patches/v7_1/set_student_guardian.py b/erpnext/patches/v7_1/set_student_guardian.py deleted file mode 100644 index 093c0bf6d9..0000000000 --- a/erpnext/patches/v7_1/set_student_guardian.py +++ /dev/null @@ -1,23 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.exists("DocType", "Guardian"): - - # 'Schools' module changed to the 'Education' - # frappe.reload_doc("schools", "doctype", "student") - # frappe.reload_doc("schools", "doctype", "student_guardian") - # frappe.reload_doc("schools", "doctype", "student_sibling") - - frappe.reload_doc("education", "doctype", "student") - frappe.reload_doc("education", "doctype", "student_guardian") - frappe.reload_doc("education", "doctype", "student_sibling") - if "student" not in frappe.db.get_table_columns("Guardian"): - return - guardian = frappe.get_all("Guardian", fields=["name", "student"]) - for d in guardian: - if d.student: - student = frappe.get_doc("Student", d.student) - if student: - student.append("guardians", {"guardian": d.name}) - student.save() diff --git a/erpnext/patches/v7_1/set_total_amount_currency_in_je.py b/erpnext/patches/v7_1/set_total_amount_currency_in_je.py deleted file mode 100644 index 8426ddcd7d..0000000000 --- a/erpnext/patches/v7_1/set_total_amount_currency_in_je.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import unicode_literals -import frappe -from erpnext import get_default_currency - -def execute(): - frappe.reload_doc("accounts", "doctype", "journal_entry") - - frappe.db.sql(""" update `tabJournal Entry` set total_amount_currency = %s - where ifnull(multi_currency, 0) = 0 - and (pay_to_recd_from is not null or pay_to_recd_from != "") """, get_default_currency()) - - for je in frappe.db.sql(""" select name from `tabJournal Entry` where multi_currency = 1 - and (pay_to_recd_from is not null or pay_to_recd_from != "")""", as_dict=1): - - doc = frappe.get_doc("Journal Entry", je.name) - for d in doc.get('accounts'): - if d.party_type and d.party: - total_amount_currency = d.account_currency - - elif frappe.db.get_value("Account", d.account, "account_type") in ["Bank", "Cash"]: - total_amount_currency = d.account_currency - - frappe.db.set_value("Journal Entry", je.name, "total_amount_currency", - total_amount_currency, update_modified=False) diff --git a/erpnext/patches/v7_1/update_bom_base_currency.py b/erpnext/patches/v7_1/update_bom_base_currency.py deleted file mode 100644 index 9a59209ea5..0000000000 --- a/erpnext/patches/v7_1/update_bom_base_currency.py +++ /dev/null @@ -1,20 +0,0 @@ -from __future__ import unicode_literals -import frappe -from erpnext import get_default_currency - -def execute(): - frappe.reload_doc("manufacturing", "doctype", "bom") - frappe.reload_doc("manufacturing", "doctype", "bom_item") - frappe.reload_doc("manufacturing", "doctype", "bom_explosion_item") - frappe.reload_doc("manufacturing", "doctype", "bom_operation") - frappe.reload_doc("manufacturing", "doctype", "bom_scrap_item") - - frappe.db.sql(""" update `tabBOM Operation` set base_hour_rate = hour_rate, - base_operating_cost = operating_cost """) - - frappe.db.sql(""" update `tabBOM Item` set base_rate = rate, base_amount = amount """) - frappe.db.sql(""" update `tabBOM Scrap Item` set base_rate = rate, base_amount = amount """) - - frappe.db.sql(""" update `tabBOM` set `tabBOM`.base_operating_cost = `tabBOM`.operating_cost, - `tabBOM`.base_raw_material_cost = `tabBOM`.raw_material_cost, - `tabBOM`.currency = (select default_currency from `tabCompany` where name = `tabBOM`.company)""") diff --git a/erpnext/patches/v7_1/update_component_type.py b/erpnext/patches/v7_1/update_component_type.py deleted file mode 100644 index 24ca0570e0..0000000000 --- a/erpnext/patches/v7_1/update_component_type.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.utils import flt - -def execute(): - frappe.reload_doc('Payroll', 'doctype', 'salary_component') - sal_components = frappe.db.sql(""" - select DISTINCT salary_component, parentfield from `tabSalary Detail`""", as_dict=True) - - if sal_components: - for sal_component in sal_components: - if sal_component.parentfield == "earnings": - frappe.db.sql("""update `tabSalary Component` set type='Earning' where salary_component=%(sal_comp)s""",{"sal_comp": sal_component.salary_component}) - else: - frappe.db.sql("""update `tabSalary Component` set type='Deduction' where salary_component=%(sal_comp)s""",{"sal_comp": sal_component.salary_component}) \ No newline at end of file diff --git a/erpnext/patches/v7_1/update_invoice_status.py b/erpnext/patches/v7_1/update_invoice_status.py deleted file mode 100644 index 851af80f7a..0000000000 --- a/erpnext/patches/v7_1/update_invoice_status.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 - -def execute(): - frappe.reload_doc('accounts', 'doctype', 'sales_invoice') - frappe.reload_doc('accounts', 'doctype', 'purchase_invoice') - - frappe.db.sql(""" - update - `tabPurchase Invoice` - set - status = (Case When outstanding_amount = 0 and docstatus = 1 and is_return = 0 then 'Paid' - when due_date < CURDATE() and outstanding_amount > 0 and docstatus =1 then 'Overdue' - when due_date >= CURDATE() and outstanding_amount > 0 and docstatus =1 then 'Unpaid' - when outstanding_amount < 0 and docstatus =1 then 'Debit Note Issued' - when is_return = 1 and docstatus =1 then 'Return' - when docstatus = 2 then 'Cancelled' - else 'Draft' - End)""") - - frappe.db.sql(""" - update - `tabSales Invoice` - set status = (Case When outstanding_amount = 0 and docstatus = 1 and is_return = 0 then 'Paid' - when due_date < CURDATE() and outstanding_amount > 0 and docstatus =1 then 'Overdue' - when due_date >= CURDATE() and outstanding_amount > 0 and docstatus =1 then 'Unpaid' - when outstanding_amount < 0 and docstatus =1 then 'Credit Note Issued' - when is_return = 1 and docstatus =1 then 'Return' - when docstatus = 2 then 'Cancelled' - else 'Draft' - End)""") \ No newline at end of file diff --git a/erpnext/patches/v7_1/update_lead_source.py b/erpnext/patches/v7_1/update_lead_source.py deleted file mode 100644 index a2a48a62e1..0000000000 --- a/erpnext/patches/v7_1/update_lead_source.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe import _ - -def execute(): - from erpnext.setup.setup_wizard.operations.install_fixtures import default_lead_sources - - frappe.reload_doc('crm', 'doctype', 'lead_source') - - frappe.local.lang = frappe.db.get_default("lang") or 'en' - - for s in default_lead_sources: - insert_lead_source(_(s)) - - # get lead sources in existing forms (customized) - # and create a document if not created - for d in ['Lead', 'Opportunity', 'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']: - sources = frappe.db.sql_list('select distinct source from `tab{0}`'.format(d)) - for s in sources: - if s and s not in default_lead_sources: - insert_lead_source(s) - - # remove customization for source - for p in frappe.get_all('Property Setter', {'doc_type':d, 'field_name':'source', 'property':'options'}): - frappe.delete_doc('Property Setter', p.name) - -def insert_lead_source(s): - if not frappe.db.exists('Lead Source', s): - frappe.get_doc(dict(doctype='Lead Source', source_name=s)).insert() diff --git a/erpnext/patches/v7_1/update_missing_salary_component_type.py b/erpnext/patches/v7_1/update_missing_salary_component_type.py deleted file mode 100644 index 824f2b881f..0000000000 --- a/erpnext/patches/v7_1/update_missing_salary_component_type.py +++ /dev/null @@ -1,50 +0,0 @@ -from __future__ import unicode_literals - -import frappe -from frappe.utils import cstr - -''' -Some components do not have type set, try and guess whether they turn up in -earnings or deductions in existing salary slips -''' - -def execute(): - frappe.reload_doc("accounts", "doctype", "salary_component_account") - frappe.reload_doc("Payroll", "doctype", "salary_component") - frappe.reload_doc("Payroll", "doctype", "taxable_salary_slab") - - for s in frappe.db.sql('''select name, type, salary_component_abbr from `tabSalary Component` - where ifnull(type, "")="" or ifnull(salary_component_abbr, "") = ""''', as_dict=1): - - component = frappe.get_doc('Salary Component', s.name) - - # guess - if not s.type: - guess = frappe.db.sql('''select - parentfield from `tabSalary Detail` - where salary_component=%s limit 1''', s.name) - - if guess: - component.type = 'Earning' if guess[0][0]=='earnings' else 'Deduction' - - else: - component.type = 'Deduction' - - if not s.salary_component_abbr: - abbr = ''.join([c[0] for c in component.salary_component.split()]).upper() - - abbr_count = frappe.db.sql(""" - select - count(name) - from - `tabSalary Component` - where - salary_component_abbr = %s or salary_component_abbr like %s - """, (abbr, abbr + "-%%")) - - if abbr_count and abbr_count[0][0] > 0: - abbr = abbr + "-" + cstr(abbr_count[0][0]) - - component.salary_component_abbr = abbr - - component.save() diff --git a/erpnext/patches/v7_1/update_portal_roles.py b/erpnext/patches/v7_1/update_portal_roles.py deleted file mode 100644 index 482586b8ef..0000000000 --- a/erpnext/patches/v7_1/update_portal_roles.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype('Role') - frappe.reload_doctype('User') - for role_name in ('Customer', 'Supplier', 'Student'): - if frappe.db.exists('Role', role_name): - frappe.db.set_value('Role', role_name, 'desk_access', 0) - else: - frappe.get_doc(dict(doctype='Role', role_name=role_name, desk_access=0)).insert() - - - # set customer, supplier roles - for c in frappe.get_all('Contact', fields=['user'], filters={'ifnull(user, "")': ('!=', '')}): - user = frappe.get_doc('User', c.user) - user.flags.ignore_validate = True - user.flags.ignore_mandatory = True - user.save() - - diff --git a/erpnext/patches/v7_1/update_total_billing_hours.py b/erpnext/patches/v7_1/update_total_billing_hours.py deleted file mode 100644 index b9c96028f5..0000000000 --- a/erpnext/patches/v7_1/update_total_billing_hours.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('projects', 'doctype', 'timesheet_detail') - frappe.reload_doc('accounts', 'doctype', 'sales_invoice_timesheet') - - frappe.db.sql("""update tabTimesheet set total_billable_hours=total_hours - where total_billable_amount>0 and docstatus = 1""") - - frappe.db.sql("""update `tabTimesheet Detail` set billing_hours=hours where docstatus < 2""") - - frappe.db.sql(""" update `tabSales Invoice Timesheet` set billing_hours = (select total_billable_hours from `tabTimesheet` - where name = time_sheet) where time_sheet is not null""") \ No newline at end of file diff --git a/erpnext/patches/v7_2/__init__.py b/erpnext/patches/v7_2/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v7_2/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py b/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py deleted file mode 100644 index d2583b9422..0000000000 --- a/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - # frappe.reload_doctype('Salary Slip', 'Salary Component') - frappe.reload_doc("Payroll", "doctype", "Salary Slip") - frappe.reload_doc("Payroll", "doctype", "Salary Component") - salary_components = [['Arrear', "ARR"], ['Leave Encashment', 'LENC']] - for salary_component, salary_abbr in salary_components: - if not frappe.db.exists('Salary Component', salary_component): - sal_comp = frappe.get_doc({ - "doctype": "Salary Component", - "salary_component": salary_component, - "type": "Earning", - "salary_component_abbr": salary_abbr - }).insert() - - salary_slips = frappe.db.sql("""select name, arrear_amount, leave_encashment_amount from `tabSalary Slip` - where docstatus !=2 and (arrear_amount > 0 or leave_encashment_amount > 0)""", as_dict=True) - - for salary_slip in salary_slips: - doc = frappe.get_doc('Salary Slip', salary_slip.name) - - if salary_slip.get("arrear_amount") > 0: - r = doc.append('earnings', { - 'salary_component': 'Arrear', - 'amount': salary_slip.arrear_amount - }) - r.db_update() - - if salary_slip.get("leave_encashment_amount") > 0: - r = doc.append('earnings', { - 'salary_component': 'Leave Encashment', - 'amount': salary_slip.leave_encashment_amount - }) - r.db_update() \ No newline at end of file diff --git a/erpnext/patches/v7_2/contact_address_links.py b/erpnext/patches/v7_2/contact_address_links.py deleted file mode 100644 index 200434c208..0000000000 --- a/erpnext/patches/v7_2/contact_address_links.py +++ /dev/null @@ -1,32 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_links -from frappe.utils import update_progress_bar - -def execute(): - frappe.reload_doc('core', 'doctype', 'dynamic_link') - frappe.reload_doc('contacts', 'doctype', 'contact') - frappe.reload_doc('contacts', 'doctype', 'address') - map_fields = ( - ('Customer', 'customer'), - ('Supplier', 'supplier'), - ('Lead', 'lead'), - ('Sales Partner', 'sales_partner') - ) - for doctype in ('Contact', 'Address'): - if frappe.db.has_column(doctype, 'customer'): - items = frappe.get_all(doctype) - for i, doc in enumerate(items): - doc = frappe.get_doc(doctype, doc.name) - dirty = False - for field in map_fields: - if doc.get(field[1]): - doc.append('links', dict(link_doctype=field[0], link_name=doc.get(field[1]))) - dirty = True - - if dirty: - deduplicate_dynamic_links(doc) - doc.update_children() - - update_progress_bar('Updating {0}'.format(doctype), i, len(items)) - print \ No newline at end of file diff --git a/erpnext/patches/v7_2/delete_fleet_management_module_def.py b/erpnext/patches/v7_2/delete_fleet_management_module_def.py deleted file mode 100644 index 542ac11e3f..0000000000 --- a/erpnext/patches/v7_2/delete_fleet_management_module_def.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - -def execute(): - if frappe.db.exists('Module Def', 'Fleet Management'): - frappe.db.sql("""delete from `tabModule Def` - where module_name = 'Fleet Management'""") \ No newline at end of file diff --git a/erpnext/patches/v7_2/empty_supplied_items_for_non_subcontracted.py b/erpnext/patches/v7_2/empty_supplied_items_for_non_subcontracted.py deleted file mode 100644 index ec6f8afc3a..0000000000 --- a/erpnext/patches/v7_2/empty_supplied_items_for_non_subcontracted.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - for doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt"]: - child_table = 'Purchase Receipt Item Supplied' if doctype != 'Purchase Order' else 'Purchase Order Item Supplied' - for data in frappe.db.sql(""" select distinct `tab{doctype}`.name from `tab{doctype}` , `tab{child_table}` - where `tab{doctype}`.name = `tab{child_table}`.parent and `tab{doctype}`.docstatus != 2 - and `tab{doctype}`.is_subcontracted = 'No' """.format(doctype = doctype, child_table = child_table), as_dict=1): - frappe.db.sql(""" delete from `tab{child_table}` - where parent = %s and parenttype = %s""".format(child_table= child_table), (data.name, doctype)) \ No newline at end of file diff --git a/erpnext/patches/v7_2/make_all_assessment_group.py b/erpnext/patches/v7_2/make_all_assessment_group.py deleted file mode 100644 index f3ec628374..0000000000 --- a/erpnext/patches/v7_2/make_all_assessment_group.py +++ /dev/null @@ -1,14 +0,0 @@ -# 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 - -def execute(): - if not frappe.db.exists({"doctype": "Assessment Group","assessment_group_name": "All Assessment Groups"}): - frappe.reload_doc("education", "doctype", "assessment_group") - doc = frappe.new_doc("Assessment Group") - doc.assessment_group_name = "All Assessment Groups" - doc.is_group = 1 - doc.flags.ignore_mandatory = True - doc.save() \ No newline at end of file diff --git a/erpnext/patches/v7_2/mark_students_active.py b/erpnext/patches/v7_2/mark_students_active.py deleted file mode 100644 index 7289e4a915..0000000000 --- a/erpnext/patches/v7_2/mark_students_active.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - # 'Schools' module changed to the 'Education' - # frappe.reload_doc('schools', 'doctype', 'student_group_student') - - frappe.reload_doc('education', 'doctype', 'student_group_student') - frappe.db.sql("update `tabStudent Group Student` set active=1") diff --git a/erpnext/patches/v7_2/rename_att_date_attendance.py b/erpnext/patches/v7_2/rename_att_date_attendance.py deleted file mode 100644 index 7f06d8f123..0000000000 --- a/erpnext/patches/v7_2/rename_att_date_attendance.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import update_reports, update_users_report_view_settings, update_property_setters - -def execute(): - if "att_date" not in frappe.db.get_table_columns("Attendance"): - return - frappe.reload_doc("hr", "doctype", "attendance") - frappe.db.sql("""update `tabAttendance` - set attendance_date = att_date - where attendance_date is null or attendance_date = '0000-00-00'""") - - update_reports("Attendance", "att_date", "attendance_date") - update_users_report_view_settings("Attendance", "att_date", "attendance_date") - update_property_setters("Attendance", "att_date", "attendance_date") diff --git a/erpnext/patches/v7_2/rename_evaluation_criteria.py b/erpnext/patches/v7_2/rename_evaluation_criteria.py deleted file mode 100644 index c6520b1b72..0000000000 --- a/erpnext/patches/v7_2/rename_evaluation_criteria.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import rename_field - -def execute(): - # 'Schools' module changed to the 'Education' - - - frappe.rename_doc("DocType", "Evaluation Criteria", "Assessment Criteria", force=True) - # frappe.reload_doc("schools", "doctype", "assessment_criteria") - frappe.reload_doc("education", "doctype", "assessment_criteria") - if 'evaluation_criteria' in frappe.db.get_table_columns('Assessment Criteria'): - rename_field("Assessment Criteria", "evaluation_criteria", "assessment_criteria") - - frappe.rename_doc("DocType", "Assessment Evaluation Criteria", "Assessment Plan Criteria", force=True) - # frappe.reload_doc("schools", "doctype", "assessment_plan_criteria") - frappe.reload_doc("education", "doctype", "assessment_plan_criteria") - if 'evaluation_criteria' in frappe.db.get_table_columns('Assessment Plan'): - rename_field("Assessment Plan Criteria", "evaluation_criteria", "assessment_criteria") - - # frappe.reload_doc("schools", "doctype", "assessment_plan") - frappe.reload_doc("education", "doctype", "assessment_plan") - rename_field("Assessment Plan", "evaluation_criterias", "assessment_criteria") - - # frappe.reload_doc("schools", "doctype", "assessment_result_detail") - frappe.reload_doc("education", "doctype", "assessment_result_detail") - if 'evaluation_criteria' in frappe.db.get_table_columns('Assessment Result Detail'): - rename_field("Assessment Result Detail", "evaluation_criteria", "assessment_criteria") - - frappe.rename_doc("DocType", "Course Evaluation Criteria", "Course Assessment Criteria", force=True) - # frappe.reload_doc("schools", "doctype", "course_assessment_criteria") - frappe.reload_doc("education", "doctype", "course_assessment_criteria") - if 'evaluation_criteria' in frappe.db.get_table_columns('Course Assessment Criteria'): - rename_field("Course Assessment Criteria", "evaluation_criteria", "assessment_criteria") - - # frappe.reload_doc("schools", "doctype", "course") - frappe.reload_doc("education", "doctype", "course") - if 'evaluation_criteria' in frappe.db.get_table_columns('Course'): - rename_field("Course", "evaluation_criterias", "assessment_criteria") diff --git a/erpnext/patches/v7_2/set_null_value_to_fields.py b/erpnext/patches/v7_2/set_null_value_to_fields.py deleted file mode 100644 index 6388be438d..0000000000 --- a/erpnext/patches/v7_2/set_null_value_to_fields.py +++ /dev/null @@ -1,11 +0,0 @@ -# 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 - -def execute(): - fields = {"Cost Center": "project", "Project": "cost_center"} - for budget_against, field in fields.items(): - frappe.db.sql(""" update `tabBudget` set {field} = null - where budget_against = %s """.format(field = field), budget_against) diff --git a/erpnext/patches/v7_2/setup_auto_close_settings.py b/erpnext/patches/v7_2/setup_auto_close_settings.py deleted file mode 100644 index 4eef2b9c8a..0000000000 --- a/erpnext/patches/v7_2/setup_auto_close_settings.py +++ /dev/null @@ -1,18 +0,0 @@ -# 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 - -def execute(): - # update the selling settings and set the close_opportunity_after_days - frappe.reload_doc("selling", "doctype", "selling_settings") - frappe.db.set_value("Selling Settings", "Selling Settings", "close_opportunity_after_days", 15) - - # Auto close Replied opportunity - frappe.db.sql("""update `tabOpportunity` set status='Closed' where status='Replied' - and date_sub(curdate(), interval 15 Day)>modified""") - - # create Support Settings doctype and update close_issue_after_days - frappe.reload_doc("support", "doctype", "support_settings") - frappe.db.set_value("Support Settings", "Support Settings", "close_issue_after_days", 7) \ No newline at end of file diff --git a/erpnext/patches/v7_2/stock_uom_in_selling.py b/erpnext/patches/v7_2/stock_uom_in_selling.py deleted file mode 100644 index d029555747..0000000000 --- a/erpnext/patches/v7_2/stock_uom_in_selling.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype('Sales Order') - frappe.reload_doctype('Sales Invoice') - frappe.reload_doctype('Quotation') - frappe.reload_doctype('Delivery Note') - - doctype_list = ['Sales Order Item', 'Delivery Note Item', 'Quotation Item', 'Sales Invoice Item'] - - for doctype in doctype_list: - frappe.reload_doctype(doctype) - frappe.db.sql("""update `tab{doctype}` - set uom = stock_uom, conversion_factor = 1, stock_qty = qty""".format(doctype=doctype)) \ No newline at end of file diff --git a/erpnext/patches/v7_2/update_abbr_in_salary_slips.py b/erpnext/patches/v7_2/update_abbr_in_salary_slips.py deleted file mode 100644 index 57432fe986..0000000000 --- a/erpnext/patches/v7_2/update_abbr_in_salary_slips.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('Payroll', 'doctype', 'Salary Slip') - if not frappe.db.has_column('Salary Detail', 'abbr'): - return - - salary_details = frappe.db.sql("""select abbr, salary_component, name from `tabSalary Detail` - where abbr is null or abbr = ''""", as_dict=True) - - for salary_detail in salary_details: - salary_component_abbr = frappe.get_value("Salary Component", salary_detail.salary_component, "salary_component_abbr") - frappe.db.sql("""update `tabSalary Detail` set abbr = %s where name = %s""",(salary_component_abbr, salary_detail.name)) \ No newline at end of file diff --git a/erpnext/patches/v7_2/update_assessment_modules.py b/erpnext/patches/v7_2/update_assessment_modules.py deleted file mode 100644 index 2b5e774d46..0000000000 --- a/erpnext/patches/v7_2/update_assessment_modules.py +++ /dev/null @@ -1,51 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import rename_field - -def execute(): - #Rename Grading Structure to Grading Scale - if not frappe.db.exists("DocType", "Grading Scale"): - frappe.rename_doc("DocType", "Grading Structure", "Grading Scale", force=True) - if not frappe.db.exists("DocType", "Grading Scale Interval"): - frappe.rename_doc("DocType", "Grade Interval", "Grading Scale Interval", force=True) - - # frappe.reload_doc("schools", "doctype", "grading_scale_interval") - frappe.reload_doc("education", "doctype", "grading_scale_interval") - if "to_score" in frappe.db.get_table_columns("Grading Scale Interval"): - rename_field("Grading Scale Interval", "to_score", "threshold") - - if not frappe.db.exists("DocType", "Assessment Plan"): - frappe.rename_doc("DocType", "Assessment", "Assessment Plan", force=True) - - # 'Schools' module changed to the 'Education' - # frappe.reload_doc("schools", "doctype", "assessment_plan") - - #Rename Assessment Results - frappe.reload_doc("education", "doctype", "assessment_plan") - if "grading_structure" in frappe.db.get_table_columns("Assessment Plan"): - rename_field("Assessment Plan", "grading_structure", "grading_scale") - - # frappe.reload_doc("schools", "doctype", "assessment_result") - # frappe.reload_doc("schools", "doctype", "assessment_result_detail") - # frappe.reload_doc("schools", "doctype", "assessment_criteria") - frappe.reload_doc("education", "doctype", "assessment_result") - frappe.reload_doc("education", "doctype", "assessment_result_detail") - frappe.reload_doc("education", "doctype", "assessment_criteria") - - - for assessment in frappe.get_all("Assessment Plan", - fields=["name", "grading_scale"], filters = [["docstatus", "!=", 2 ]]): - for stud_result in frappe.db.sql("select * from `tabAssessment Result` where parent= %s", - assessment.name, as_dict=True): - if stud_result.result: - assessment_result = frappe.new_doc("Assessment Result") - assessment_result.student = stud_result.student - assessment_result.student_name = stud_result.student_name - assessment_result.assessment_plan = assessment.name - assessment_result.grading_scale = assessment.grading_scale - assessment_result.total_score = stud_result.result - assessment_result.flags.ignore_validate = True - assessment_result.flags.ignore_mandatory = True - assessment_result.save() - - frappe.db.sql("""delete from `tabAssessment Result` where parent != '' or parent is not null""") \ No newline at end of file diff --git a/erpnext/patches/v7_2/update_attendance_docstatus.py b/erpnext/patches/v7_2/update_attendance_docstatus.py deleted file mode 100644 index a69052657d..0000000000 --- a/erpnext/patches/v7_2/update_attendance_docstatus.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("education", "doctype", "student_attendance") - frappe.db.sql(''' - update `tabStudent Attendance` set - docstatus=0 - where - docstatus=1''') \ No newline at end of file diff --git a/erpnext/patches/v7_2/update_doctype_status.py b/erpnext/patches/v7_2/update_doctype_status.py deleted file mode 100644 index c66f3f2e73..0000000000 --- a/erpnext/patches/v7_2/update_doctype_status.py +++ /dev/null @@ -1,11 +0,0 @@ -# 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 - -def execute(): - doctypes = ["Opportunity", "Quotation", "Sales Order", "Sales Invoice", "Purchase Invoice", "Purchase Order", "Delivery Note", "Purchase Receipt"] - for doctype in doctypes: - frappe.db.sql(""" update `tab{doctype}` set status = 'Draft' - where status = 'Cancelled' and docstatus = 0 """.format(doctype = doctype)) \ No newline at end of file diff --git a/erpnext/patches/v7_2/update_guardian_name_in_student_master.py b/erpnext/patches/v7_2/update_guardian_name_in_student_master.py deleted file mode 100644 index 9f589ef00e..0000000000 --- a/erpnext/patches/v7_2/update_guardian_name_in_student_master.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import rename_field - -def execute(): - # 'Schools' module changed to the 'Education' - # frappe.reload_doc("schools", "doctype", "student_guardian") - frappe.reload_doc("education", "doctype", "student_guardian") - - student_guardians = frappe.get_all("Student Guardian", fields=["guardian"]) - for student_guardian in student_guardians: - guardian_name = frappe.db.get_value("Guardian", student_guardian.guardian, "guardian_name") - frappe.db.sql("update `tabStudent Guardian` set guardian_name = %s where guardian= %s", - (guardian_name, student_guardian.guardian)) \ No newline at end of file diff --git a/erpnext/patches/v7_2/update_party_type.py b/erpnext/patches/v7_2/update_party_type.py deleted file mode 100644 index 147f5a3643..0000000000 --- a/erpnext/patches/v7_2/update_party_type.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('setup', 'doctype', 'party_type') - make_party_type() - -def make_party_type(): - for party_type in ["Customer", "Supplier", "Employee"]: - if not frappe.db.get_value("Party Type", party_type): - doc = frappe.new_doc("Party Type") - doc.party_type = party_type - doc.save(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/patches/v7_2/update_salary_slips.py b/erpnext/patches/v7_2/update_salary_slips.py deleted file mode 100644 index 9fcce62d8f..0000000000 --- a/erpnext/patches/v7_2/update_salary_slips.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import unicode_literals -import frappe -from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_details -from frappe.utils import cint - -def execute(): - frappe.reload_doc("Payroll", "doctype", "Salary Slip") - if not frappe.db.has_column('Salary Slip', 'fiscal_year'): - return - - salary_slips = frappe.db.sql("""select month, name, fiscal_year from `tabSalary Slip` - where (month is not null and month != '') and - start_date is null and end_date is null and docstatus != 2""", as_dict=True) - - for salary_slip in salary_slips: - if not cint(salary_slip.month): - continue - get_start_end_date = get_month_details(salary_slip.fiscal_year, cint(salary_slip.month)) - start_date = get_start_end_date['month_start_date'] - end_date = get_start_end_date['month_end_date'] - frappe.db.sql("""update `tabSalary Slip` set start_date = %s, end_date = %s where name = %s""", - (start_date, end_date, salary_slip.name)) \ No newline at end of file diff --git a/erpnext/patches/v7_2/update_website_for_variant.py b/erpnext/patches/v7_2/update_website_for_variant.py deleted file mode 100644 index e8eef6e7da..0000000000 --- a/erpnext/patches/v7_2/update_website_for_variant.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - # variant must have show_in_website = 0 - frappe.reload_doctype('Item') - frappe.db.sql(''' - update tabItem set - show_variant_in_website = 1, - show_in_website = 0 - where - show_in_website=1 - and ifnull(variant_of, "")!=""''') \ No newline at end of file diff --git a/erpnext/patches/v8_0/__init__.py b/erpnext/patches/v8_0/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v8_0/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v8_0/addresses_linked_to_lead.py b/erpnext/patches/v8_0/addresses_linked_to_lead.py deleted file mode 100644 index b5f2234228..0000000000 --- a/erpnext/patches/v8_0/addresses_linked_to_lead.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.db.sql("""UPDATE `tabDynamic Link` SET link_doctype = 'Lead' WHERE link_doctype = 'Load'""") diff --git a/erpnext/patches/v8_0/change_in_words_varchar_length.py b/erpnext/patches/v8_0/change_in_words_varchar_length.py deleted file mode 100644 index 68ff95b5ed..0000000000 --- a/erpnext/patches/v8_0/change_in_words_varchar_length.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - doctypes = frappe.db.sql_list("""select parent from tabDocField where fieldname = 'in_words'""") - - for dt in doctypes: - for fieldname in ("in_words", "base_in_words"): - frappe.db.sql("alter table `tab{0}` change column `{1}` `{2}` varchar(255)" - .format(dt, fieldname, fieldname)) - - frappe.db.sql("""alter table `tabJournal Entry` - change column `total_amount_in_words` `total_amount_in_words` varchar(255)""") diff --git a/erpnext/patches/v8_0/create_address_doc_from_address_field_in_company.py b/erpnext/patches/v8_0/create_address_doc_from_address_field_in_company.py deleted file mode 100644 index cf1bc5af05..0000000000 --- a/erpnext/patches/v8_0/create_address_doc_from_address_field_in_company.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - # new field address_html is created in place of address field for the company's address in PR #8754 (without patch) - # so here is the patch for moving the address details in the address doc - company_list = [] - if 'address' in frappe.db.get_table_columns('Company'): - company_list = frappe.db.sql('''select name, address from `tabCompany` - where address is not null and address != ""''', as_dict=1) - - for company in company_list: - add_list = company.address.split(" ") - if ',' in company.address: - add_list = company.address.rpartition(',') - elif ' ' in company.address: - add_list = company.address.rpartition(' ') - else: - add_list = [company.address, None, company.address] - - doc = frappe.get_doc({ - "doctype":"Address", - "address_line1": add_list[0], - "city": add_list[2], - "links": [{ - "link_doctype": "Company", - "link_name": company.name - }] - }) - doc.save() diff --git a/erpnext/patches/v8_0/create_domain_docs.py b/erpnext/patches/v8_0/create_domain_docs.py deleted file mode 100644 index 3ef4f3c1bb..0000000000 --- a/erpnext/patches/v8_0/create_domain_docs.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -import erpnext - -def execute(): - """Create domain documents""" - frappe.reload_doc("core", "doctype", "domain") - frappe.reload_doc("core", "doctype", "domain_settings") - frappe.reload_doc("core", "doctype", "has_domain") - frappe.reload_doc("core", "doctype", "role") - - for domain in ("Distribution", "Manufacturing", "Retail", "Services", "Education"): - if not frappe.db.exists({"doctype": "Domain", "domain": domain}): - create_domain(domain) - - # set domain in domain settings based on company domain - - domains = [] - condition = "" - company = erpnext.get_default_company() - if company: - condition = " and name={0}".format(frappe.db.escape(company)) - - domains = frappe.db.sql_list("select distinct domain from `tabCompany` where domain != 'Other' {0}".format(condition)) - - if not domains: - return - - domain_settings = frappe.get_doc("Domain Settings", "Domain Settings") - checked_domains = [row.domain for row in domain_settings.active_domains] - - for domain in domains: - # check and ignore if the domains is already checked in domain settings - if domain in checked_domains: - continue - - if not frappe.db.get_value("Domain", domain): - # user added custom domain in companies domain field - create_domain(domain) - - row = domain_settings.append("active_domains", dict(domain=domain)) - - domain_settings.save(ignore_permissions=True) - -def create_domain(domain): - # create new domain - - doc = frappe.new_doc("Domain") - doc.domain = domain - doc.db_update() \ No newline at end of file diff --git a/erpnext/patches/v8_0/delete_bin_indexes.py b/erpnext/patches/v8_0/delete_bin_indexes.py deleted file mode 100644 index 12cacdb952..0000000000 --- a/erpnext/patches/v8_0/delete_bin_indexes.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt -# -*- coding: utf-8 -*- - -from __future__ import unicode_literals -import frappe - -def execute(): - # delete bin indexes - unwanted_indexes = ["item_code", "warehouse"] - - for k in unwanted_indexes: - try: - frappe.db.sql("drop index {0} on `tabBin`".format(k)) - except: - pass \ No newline at end of file diff --git a/erpnext/patches/v8_0/delete_schools_depricated_doctypes.py b/erpnext/patches/v8_0/delete_schools_depricated_doctypes.py deleted file mode 100644 index 09a78ed3ca..0000000000 --- a/erpnext/patches/v8_0/delete_schools_depricated_doctypes.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2017, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - """ delete doctypes """ - - if frappe.db.exists("DocType", "Grading Structure"): - frappe.delete_doc("DocType", "Grading Structure", force=1) - - if frappe.db.exists("DocType", "Grade Interval"): - frappe.delete_doc("DocType", "Grade Interval", force=1) \ No newline at end of file diff --git a/erpnext/patches/v8_0/disable_instructor_role.py b/erpnext/patches/v8_0/disable_instructor_role.py deleted file mode 100644 index 4ba78d172c..0000000000 --- a/erpnext/patches/v8_0/disable_instructor_role.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - """ - disable the instructor role for companies with domain other than - Education. - """ - - domains = frappe.db.sql_list("select domain from tabCompany") - if "Education" not in domains: - if frappe.db.exists("Role", "Instructor"): - role = frappe.get_doc("Role", "Instructor") - role.disabled = 1 - role.save(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/patches/v8_0/enable_booking_asset_depreciation_automatically.py b/erpnext/patches/v8_0/enable_booking_asset_depreciation_automatically.py deleted file mode 100644 index 1088d702dd..0000000000 --- a/erpnext/patches/v8_0/enable_booking_asset_depreciation_automatically.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.db.set_value("Accounts Settings", None, - "book_asset_depreciation_entry_automatically", 1) \ No newline at end of file diff --git a/erpnext/patches/v8_0/fix_status_for_invoices_with_negative_outstanding.py b/erpnext/patches/v8_0/fix_status_for_invoices_with_negative_outstanding.py deleted file mode 100644 index 2e7f360c97..0000000000 --- a/erpnext/patches/v8_0/fix_status_for_invoices_with_negative_outstanding.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - for dt, status in [["Sales Invoice", "Credit Note Issued"], ["Purchase Invoice", "Debit Note Issued"]]: - invoices = frappe.db.sql(""" - select name - from `tab{0}` - where - status = %s - and outstanding_amount < 0 - and docstatus=1 - and is_return=0 - """.format(dt), status) - - for inv in invoices: - return_inv = frappe.db.sql("""select name from `tab{0}` - where is_return=1 and return_against=%s and docstatus=1""".format(dt), inv[0]) - if not return_inv: - frappe.db.sql("update `tab{0}` set status='Paid' where name = %s".format(dt), inv[0]) \ No newline at end of file diff --git a/erpnext/patches/v8_0/make_payments_table_blank_for_non_pos_invoice.py b/erpnext/patches/v8_0/make_payments_table_blank_for_non_pos_invoice.py deleted file mode 100644 index 9750fb7222..0000000000 --- a/erpnext/patches/v8_0/make_payments_table_blank_for_non_pos_invoice.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype('Sales Invoice') - - frappe.db.sql(""" - delete from - `tabSales Invoice Payment` - where - parent in (select name from `tabSales Invoice` where is_pos = 0) - """) \ No newline at end of file diff --git a/erpnext/patches/v8_0/merge_student_batch_and_student_group.py b/erpnext/patches/v8_0/merge_student_batch_and_student_group.py deleted file mode 100644 index fb9021fd68..0000000000 --- a/erpnext/patches/v8_0/merge_student_batch_and_student_group.py +++ /dev/null @@ -1,73 +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.model.utils.rename_field import * -from frappe.model.mapper import get_mapped_doc - - -def execute(): - # for converting student batch into student group - for doctype in ["Student Group", "Student Group Student", 'Program Enrollment', - "Student Group Instructor", "Student Attendance", "Student", "Student Batch Name"]: - # 'Schools' module changed to the 'Education' - # frappe.reload_doc("schools", "doctype", frappe.scrub(doctype)) - - frappe.reload_doc("education", "doctype", frappe.scrub(doctype)) - - if frappe.db.table_exists("Student Batch"): - student_batches = frappe.db.sql('''select name as student_group_name, student_batch_name as batch, - program, academic_year, academic_term from `tabStudent Batch`''', as_dict=1) - - for student_batch in student_batches: - # create student batch name if does not exists !! - if student_batch.get("batch") and not frappe.db.exists("Student Batch Name", student_batch.get("batch")): - frappe.get_doc({ - "doctype": "Student Batch Name", - "batch_name": student_batch.get("batch") - }).insert(ignore_permissions=True) - - student_batch.update({"doctype":"Student Group", "group_based_on": "Batch"}) - doc = frappe.get_doc(student_batch) - - if frappe.db.sql("SHOW COLUMNS FROM `tabStudent Batch Student` LIKE 'active'"): - cond = ", active" - else: - cond = " " - student_list = frappe.db.sql('''select student, student_name {cond} from `tabStudent Batch Student` - where parent=%s'''.format(cond=cond), (doc.student_group_name), as_dict=1) - - if student_list: - for i, student in enumerate(student_list): - student.update({"group_roll_number": i+1}) - doc.extend("students", student_list) - - instructor_list = None - if frappe.db.table_exists("Student Batch Instructor"): - instructor_list = frappe.db.sql('''select instructor, instructor_name from `tabStudent Batch Instructor` - where parent=%s''', (doc.student_group_name), as_dict=1) - if instructor_list: - doc.extend("instructors", instructor_list) - doc.save() - - # delete the student batch and child-table - if frappe.db.table_exists("Student Batch"): - frappe.delete_doc("DocType", "Student Batch", force=1) - if frappe.db.table_exists("Student Batch Student"): - frappe.delete_doc("DocType", "Student Batch Student", force=1) - if frappe.db.table_exists("Student Batch Instructor"): - frappe.delete_doc("DocType", "Student Batch Instructor", force=1) - - # delete the student batch creation tool - if frappe.db.table_exists("Student Batch Creation Tool"): - frappe.delete_doc("DocType", "Student Batch Creation Tool", force=1) - - # delete the student batch creation tool - if frappe.db.table_exists("Attendance Tool Student"): - frappe.delete_doc("DocType", "Attendance Tool Student", force=1) - - # change the student batch to student group in the student attendance - table_columns = frappe.db.get_table_columns("Student Attendance") - if "student_batch" in table_columns: - rename_field("Student Attendance", "student_batch", "student_group") diff --git a/erpnext/patches/v8_0/move_account_head_from_account_to_warehouse_for_inventory.py b/erpnext/patches/v8_0/move_account_head_from_account_to_warehouse_for_inventory.py deleted file mode 100644 index b59d81831f..0000000000 --- a/erpnext/patches/v8_0/move_account_head_from_account_to_warehouse_for_inventory.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Warehouse") - frappe.db.sql(""" - update - `tabWarehouse` - set - account = (select name from `tabAccount` - where account_type = 'Stock' and - warehouse = `tabWarehouse`.name and is_group = 0 limit 1)""") \ No newline at end of file diff --git a/erpnext/patches/v8_0/move_perpetual_inventory_setting.py b/erpnext/patches/v8_0/move_perpetual_inventory_setting.py deleted file mode 100644 index 78322d4575..0000000000 --- a/erpnext/patches/v8_0/move_perpetual_inventory_setting.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype('Company') - enabled = frappe.db.get_single_value("Accounts Settings", "auto_accounting_for_stock") or 0 - for data in frappe.get_all('Company', fields = ["name"]): - doc = frappe.get_doc('Company', data.name) - doc.enable_perpetual_inventory = enabled - doc.db_update() \ No newline at end of file diff --git a/erpnext/patches/v8_0/rename_is_sample_item_to_allow_zero_valuation_rate.py b/erpnext/patches/v8_0/rename_is_sample_item_to_allow_zero_valuation_rate.py deleted file mode 100644 index e517df5fdb..0000000000 --- a/erpnext/patches/v8_0/rename_is_sample_item_to_allow_zero_valuation_rate.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import rename_field - -def execute(): - - doc_list = ["Purchase Invoice Item", "Stock Entry Detail", "Delivery Note Item", - "Purchase Receipt Item", "Sales Invoice Item"] - - for doctype in doc_list: - frappe.reload_doctype(doctype) - if "is_sample_item" in frappe.db.get_table_columns(doctype): - rename_field(doctype, "is_sample_item", "allow_zero_valuation_rate") \ No newline at end of file diff --git a/erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py b/erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py deleted file mode 100644 index 5ad862a436..0000000000 --- a/erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py +++ /dev/null @@ -1,25 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.db.sql( - """ - UPDATE `tabMaterial Request` - SET status = CASE - WHEN docstatus = 2 THEN 'Cancelled' - WHEN docstatus = 0 THEN 'Draft' - ELSE CASE - WHEN status = 'Stopped' THEN 'Stopped' - WHEN status != 'Stopped' AND per_ordered = 0 THEN 'Pending' - WHEN per_ordered < 100 AND per_ordered > 0 AND status != 'Stopped' - THEN 'Partially Ordered' - WHEN per_ordered = 100 AND material_request_type = 'Purchase' - AND status != 'Stopped' THEN 'Ordered' - WHEN per_ordered = 100 AND material_request_type = 'Material Transfer' - AND status != 'Stopped' THEN 'Transferred' - WHEN per_ordered = 100 AND material_request_type = 'Material Issue' - AND status != 'Stopped' THEN 'Issued' - END - END - """ - ) \ No newline at end of file diff --git a/erpnext/patches/v8_0/rename_total_margin_to_rate_with_margin.py b/erpnext/patches/v8_0/rename_total_margin_to_rate_with_margin.py deleted file mode 100644 index 4065438796..0000000000 --- a/erpnext/patches/v8_0/rename_total_margin_to_rate_with_margin.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import unicode_literals -import frappe - -from frappe.model.utils.rename_field import rename_field - -def execute(): - """ - Rename Total Margin field to Rate With Margin in - "Sales Order Item", "Sales Invoice Item", "Delivery Note Item", - "Quotation Item" - """ - - for d in ("Sales Order Item", "Sales Invoice Item", - "Delivery Note Item", "Quotation Item"): - frappe.reload_doctype(d) - rename_field_if_exists(d, "total_margin", "rate_with_margin") - - -def rename_field_if_exists(doctype, old_fieldname, new_fieldname): - try: - rename_field(doctype, old_fieldname, new_fieldname) - except Exception as e: - if e.args[0] != 1054: - raise diff --git a/erpnext/patches/v8_0/repost_reserved_qty_for_multiple_sales_uom.py b/erpnext/patches/v8_0/repost_reserved_qty_for_multiple_sales_uom.py deleted file mode 100644 index 3030b8e2f3..0000000000 --- a/erpnext/patches/v8_0/repost_reserved_qty_for_multiple_sales_uom.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty - -def execute(): - for doctype in ("Sales Order Item", "Bin"): - frappe.reload_doctype(doctype) - - repost_for = frappe.db.sql("""select distinct item_code, warehouse - from `tabSales Order Item` where docstatus=1 and uom != stock_uom and - exists(select name from tabItem where name=`tabSales Order Item`.item_code and ifnull(is_stock_item, 0)=1)""") - - for item_code, warehouse in repost_for: - update_bin_qty(item_code, warehouse, { - "reserved_qty": get_reserved_qty(item_code, warehouse) - }) \ No newline at end of file diff --git a/erpnext/patches/v8_0/revert_manufacturers_table_from_item.py b/erpnext/patches/v8_0/revert_manufacturers_table_from_item.py deleted file mode 100644 index 60cbb33b80..0000000000 --- a/erpnext/patches/v8_0/revert_manufacturers_table_from_item.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.exists("DocType", "Item Manufacturer"): - frappe.reload_doctype("Item") - item_manufacturers = frappe.db.sql(""" - select parent, manufacturer, manufacturer_part_no - from `tabItem Manufacturer` - """, as_dict=1) - - for im in item_manufacturers: - frappe.db.sql(""" - update tabItem - set manufacturer=%s, manufacturer_part_no=%s - where name=%s - """, (im.manufacturer, im.manufacturer_part_no, im.parent)) - - frappe.delete_doc("DocType", "Item Manufacturer") \ No newline at end of file diff --git a/erpnext/patches/v8_0/save_system_settings.py b/erpnext/patches/v8_0/save_system_settings.py deleted file mode 100644 index d479ece8a6..0000000000 --- a/erpnext/patches/v8_0/save_system_settings.py +++ /dev/null @@ -1,20 +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 cint - -def execute(): - """ - save system settings document - """ - - frappe.reload_doc("core", "doctype", "system_settings") - doc = frappe.get_doc("System Settings") - doc.flags.ignore_mandatory = True - - if cint(doc.currency_precision) == 0: - doc.currency_precision = '' - - doc.save(ignore_permissions=True) diff --git a/erpnext/patches/v8_0/set_null_to_serial_nos_for_disabled_sales_invoices.py b/erpnext/patches/v8_0/set_null_to_serial_nos_for_disabled_sales_invoices.py deleted file mode 100644 index 197d6ded61..0000000000 --- a/erpnext/patches/v8_0/set_null_to_serial_nos_for_disabled_sales_invoices.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty - -def execute(): - frappe.db.sql(""" - update - `tabSales Invoice Item` - set serial_no = NULL - where - parent in (select name from `tabSales Invoice` where update_stock = 0 and docstatus = 1)""") \ No newline at end of file diff --git a/erpnext/patches/v8_0/set_project_copied_from.py b/erpnext/patches/v8_0/set_project_copied_from.py deleted file mode 100644 index d4287978cf..0000000000 --- a/erpnext/patches/v8_0/set_project_copied_from.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Project") - - frappe.db.sql(''' - UPDATE `tabProject` - SET copied_from=name - WHERE copied_from is NULL - ''') \ No newline at end of file diff --git a/erpnext/patches/v8_0/set_sales_invoice_serial_number_from_delivery_note.py b/erpnext/patches/v8_0/set_sales_invoice_serial_number_from_delivery_note.py deleted file mode 100644 index 8a4ef4086b..0000000000 --- a/erpnext/patches/v8_0/set_sales_invoice_serial_number_from_delivery_note.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty - -def execute(): - """ Set the Serial Numbers in Sales Invoice Item from Delivery Note Item """ - - frappe.reload_doc("stock", "doctype", "serial_no") - - frappe.db.sql(""" update `tabSales Invoice Item` sii inner join - `tabDelivery Note Item` dni on sii.dn_detail=dni.name and sii.qty=dni.qty - set sii.serial_no=dni.serial_no where sii.parent IN (select si.name - from `tabSales Invoice` si where si.update_stock=0 and si.docstatus=1)""") - - items = frappe.db.sql(""" select sii.parent, sii.serial_no from `tabSales Invoice Item` sii - left join `tabSales Invoice` si on sii.parent=si.name - where si.docstatus=1 and si.update_stock=0""", as_dict=True) - - for item in items: - sales_invoice = item.get("parent", None) - serial_nos = item.get("serial_no", "") - - if not sales_invoice or not serial_nos: - continue - - serial_nos = ["{}".format(frappe.db.escape(no)) for no in serial_nos.split("\n")] - - frappe.db.sql(""" - UPDATE - `tabSerial No` - SET - sales_invoice={sales_invoice} - WHERE - name in ({serial_nos}) - """.format( - sales_invoice=frappe.db.escape(sales_invoice), - serial_nos=",".join(serial_nos) - ) - ) \ No newline at end of file diff --git a/erpnext/patches/v8_0/update_customer_pos_id.py b/erpnext/patches/v8_0/update_customer_pos_id.py deleted file mode 100644 index a772ae90c5..0000000000 --- a/erpnext/patches/v8_0/update_customer_pos_id.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Customer") - frappe.db.sql(""" update `tabCustomer` set customer_pos_id = name """) \ No newline at end of file diff --git a/erpnext/patches/v8_0/update_production_orders.py b/erpnext/patches/v8_0/update_production_orders.py deleted file mode 100644 index 8e993cc102..0000000000 --- a/erpnext/patches/v8_0/update_production_orders.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - # reload schema - for doctype in ("Work Order", "Work Order Item", "Work Order Operation", - "BOM Item", "BOM Explosion Item", "BOM"): - frappe.reload_doctype(doctype) - - frappe.reload_doc("stock", "doctype", "item") - frappe.reload_doc("stock", "doctype", "item_default") - - # fetch all draft and submitted work orders - fields = ["name"] - if "source_warehouse" in frappe.db.get_table_columns("Work Order"): - fields.append("source_warehouse") - - wo_orders = frappe.get_all("Work Order", filters={"docstatus": ["!=", 2]}, fields=fields) - - count = 0 - for p in wo_orders: - wo_order = frappe.get_doc("Work Order", p.name) - count += 1 - - # set required items table - wo_order.set_required_items() - - for item in wo_order.get("required_items"): - # set source warehouse based on parent - if not item.source_warehouse and "source_warehouse" in fields: - item.source_warehouse = wo_order.get("source_warehouse") - item.db_update() - - if wo_order.docstatus == 1: - # update transferred qty based on Stock Entry, it also updates db - wo_order.update_transaferred_qty_for_required_items() - - # Set status where it was 'Unstopped', as it is deprecated - if wo_order.status == "Unstopped": - status = wo_order.get_status() - wo_order.db_set("status", status) - elif wo_order.status == "Stopped": - wo_order.update_reserved_qty_for_production() - - if count % 200 == 0: - frappe.db.commit() \ No newline at end of file diff --git a/erpnext/patches/v8_0/update_sales_cost_in_project.py b/erpnext/patches/v8_0/update_sales_cost_in_project.py deleted file mode 100644 index 1a29fc4db4..0000000000 --- a/erpnext/patches/v8_0/update_sales_cost_in_project.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("projects", "doctype", "project") - - frappe.db.sql(""" - update `tabProject` p - set total_sales_amount = ifnull((select sum(base_grand_total) - from `tabSales Order` where project=p.name and docstatus=1), 0) - """) \ No newline at end of file diff --git a/erpnext/patches/v8_0/update_status_as_paid_for_completed_expense_claim.py b/erpnext/patches/v8_0/update_status_as_paid_for_completed_expense_claim.py deleted file mode 100644 index 19d27b206b..0000000000 --- a/erpnext/patches/v8_0/update_status_as_paid_for_completed_expense_claim.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - """ set status as Paid in Expense Claim if total_sactioned_amount - and total_amount_reimbursed is equal """ - - frappe.reload_doctype('Expense Claim') - - frappe.db.sql(""" - update - `tabExpense Claim` - set status = 'Paid' - where - total_sanctioned_amount = total_amount_reimbursed - """) diff --git a/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py b/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py deleted file mode 100644 index 1f937bb8af..0000000000 --- a/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('manufacturing', 'doctype', 'bom_item') - frappe.reload_doc('manufacturing', 'doctype', 'bom_explosion_item') - frappe.reload_doc('manufacturing', 'doctype', 'bom_scrap_item') - frappe.db.sql("update `tabBOM Item` set stock_qty = qty, uom = stock_uom, conversion_factor = 1") - frappe.db.sql("update `tabBOM Explosion Item` set stock_qty = qty") - if "qty" in frappe.db.get_table_columns("BOM Scrap Item"): - frappe.db.sql("update `tabBOM Scrap Item` set stock_qty = qty") \ No newline at end of file diff --git a/erpnext/patches/v8_0/update_stock_qty_value_in_purchase_invoice.py b/erpnext/patches/v8_0/update_stock_qty_value_in_purchase_invoice.py deleted file mode 100644 index be5cf3aed7..0000000000 --- a/erpnext/patches/v8_0/update_stock_qty_value_in_purchase_invoice.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('accounts', 'doctype', 'purchase_invoice_item') - frappe.db.sql("update `tabPurchase Invoice Item` set stock_qty = qty, stock_uom = uom") \ No newline at end of file diff --git a/erpnext/patches/v8_0/update_student_groups_from_student_batches.py b/erpnext/patches/v8_0/update_student_groups_from_student_batches.py deleted file mode 100644 index ae24fe4a14..0000000000 --- a/erpnext/patches/v8_0/update_student_groups_from_student_batches.py +++ /dev/null @@ -1,38 +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.model.utils.rename_field import * -from frappe.model.mapper import get_mapped_doc - - -def execute(): - if frappe.db.table_exists("Student Batch"): - student_batches = frappe.db.sql('''select name from `tabStudent Batch`''', as_dict=1) - - for student_batch in student_batches: - if frappe.db.exists("Student Group", student_batch.get("name")): - student_group = frappe.get_doc("Student Group", student_batch.get("name")) - - if frappe.db.table_exists("Student Batch Student"): - current_student_list = frappe.db.sql_list('''select student from `tabStudent Group Student` - where parent=%s''', (student_group.name)) - batch_student_list = frappe.db.sql_list('''select student from `tabStudent Batch Student` - where parent=%s''', (student_group.name)) - - student_list = list(set(batch_student_list)-set(current_student_list)) - if student_list: - student_group.extend("students", [{"student":d} for d in student_list]) - - if frappe.db.table_exists("Student Batch Instructor"): - current_instructor_list = frappe.db.sql_list('''select instructor from `tabStudent Group Instructor` - where parent=%s''', (student_group.name)) - batch_instructor_list = frappe.db.sql_list('''select instructor from `tabStudent Batch Instructor` - where parent=%s''', (student_group.name)) - - instructor_list = list(set(batch_instructor_list)-set(current_instructor_list)) - if instructor_list: - student_group.extend("instructors", [{"instructor":d} for d in instructor_list]) - - student_group.save() diff --git a/erpnext/patches/v8_0/update_supplier_address_in_stock_entry.py b/erpnext/patches/v8_0/update_supplier_address_in_stock_entry.py deleted file mode 100644 index a2173048fd..0000000000 --- a/erpnext/patches/v8_0/update_supplier_address_in_stock_entry.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - # copy supplier_address to address_display, and set supplier_address to blank - - stock_entries = frappe.db.sql(""" select name, purchase_order, supplier_address from `tabStock Entry` - where ifnull(supplier_address, '') <> ''""", as_dict=True) - - frappe.reload_doc('stock', 'doctype', 'stock_entry') - - for stock_entry in stock_entries: - # move supplier address to address_display, and fetch the supplier address from purchase order - - se = frappe.get_doc("Stock Entry", stock_entry.get("name")) - se.address_display = stock_entry.get("supplier_address") - se.supplier_address = frappe.db.get_value("Purchase Order", stock_entry.get("purchase_order"),"supplier_address") or None - - se.db_update() diff --git a/erpnext/patches/v8_1/__init__.py b/erpnext/patches/v8_1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v8_1/add_hsn_sac_codes.py b/erpnext/patches/v8_1/add_hsn_sac_codes.py deleted file mode 100644 index 0fce96a8d4..0000000000 --- a/erpnext/patches/v8_1/add_hsn_sac_codes.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import unicode_literals -import frappe -from erpnext.regional.india.setup import setup - -def execute(): - company = frappe.get_all('Company', filters = {'country': 'India'}) - if not company: - return - - # call setup for india - setup(patch=True) \ No newline at end of file diff --git a/erpnext/patches/v8_1/add_indexes_in_transaction_doctypes.py b/erpnext/patches/v8_1/add_indexes_in_transaction_doctypes.py deleted file mode 100644 index 4631602606..0000000000 --- a/erpnext/patches/v8_1/add_indexes_in_transaction_doctypes.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - -def execute(): - for dt in ("Sales Order Item", "Purchase Order Item", - "Material Request Item", "Work Order Item", "Packed Item"): - frappe.get_doc("DocType", dt).run_module_method("on_doctype_update") \ No newline at end of file diff --git a/erpnext/patches/v8_1/allow_invoice_copy_to_edit_after_submit.py b/erpnext/patches/v8_1/allow_invoice_copy_to_edit_after_submit.py deleted file mode 100644 index 4c606af424..0000000000 --- a/erpnext/patches/v8_1/allow_invoice_copy_to_edit_after_submit.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - inv_copy_options = "ORIGINAL FOR RECIPIENT\nDUPLICATE FOR TRANSPORTER\nDUPLICATE FOR SUPPLIER\nTRIPLICATE FOR SUPPLIER" - - frappe.db.sql("""update `tabCustom Field` set allow_on_submit=1, options=%s - where fieldname='invoice_copy' and dt = 'Sales Invoice' - """, inv_copy_options) - - frappe.db.sql("""update `tabCustom Field` set read_only=1 - where fieldname='gst_state_number' and dt = 'Address' - """) diff --git a/erpnext/patches/v8_1/delete_deprecated_reports.py b/erpnext/patches/v8_1/delete_deprecated_reports.py deleted file mode 100644 index 3e0fdee719..0000000000 --- a/erpnext/patches/v8_1/delete_deprecated_reports.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - """ delete deprecated reports """ - - reports = [ - "Monthly Salary Register", "Customer Addresses And Contacts", - "Supplier Addresses And Contacts" - ] - - for report in reports: - if frappe.db.exists("Report", report): - check_and_update_auto_email_report(report) - frappe.db.commit() - - frappe.delete_doc("Report", report, ignore_permissions=True) - -def check_and_update_auto_email_report(report): - """ delete or update auto email report for deprecated report """ - - auto_email_report = frappe.db.get_value("Auto Email Report", {"report": report}) - if not auto_email_report: - return - - if report == "Monthly Salary Register": - frappe.delete_doc("Auto Email Report", auto_email_report) - - elif report in ["Customer Addresses And Contacts", "Supplier Addresses And Contacts"]: - frappe.db.set_value("Auto Email Report", auto_email_report, "report", report) \ No newline at end of file diff --git a/erpnext/patches/v8_1/gst_fixes.py b/erpnext/patches/v8_1/gst_fixes.py deleted file mode 100644 index 34255eb0a4..0000000000 --- a/erpnext/patches/v8_1/gst_fixes.py +++ /dev/null @@ -1,62 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.custom.doctype.custom_field.custom_field import create_custom_field -from erpnext.regional.address_template.setup import set_up_address_templates - - -def execute(): - company = frappe.get_all('Company', filters = {'country': 'India'}) - if not company: - return - - update_existing_custom_fields() - add_custom_fields() - set_up_address_templates(default_country='India') - frappe.reload_doc("regional", "print_format", "gst_tax_invoice") - - -def update_existing_custom_fields(): - frappe.db.sql("""update `tabCustom Field` set label = 'HSN/SAC' - where fieldname='gst_hsn_code' and label='GST HSN Code' - """) - - frappe.db.sql("""update `tabCustom Field` set print_hide = 1 - where fieldname in ('customer_gstin', 'supplier_gstin', 'company_gstin') - """) - - frappe.db.sql("""update `tabCustom Field` set insert_after = 'address_display' - where fieldname in ('customer_gstin', 'supplier_gstin') - """) - - frappe.db.sql("""update `tabCustom Field` set insert_after = 'company_address_display' - where fieldname = 'company_gstin' - """) - - frappe.db.sql("""update `tabCustom Field` set insert_after = 'description' - where fieldname='gst_hsn_code' and dt in ('Sales Invoice Item', 'Purchase Invoice Item') - """) - - -def add_custom_fields(): - hsn_sac_field = dict(fieldname='gst_hsn_code', label='HSN/SAC', - fieldtype='Data', options='item_code.gst_hsn_code', insert_after='description') - - custom_fields = { - 'Address': [ - dict(fieldname='gst_state_number', label='GST State Number', - fieldtype='Int', insert_after='gst_state'), - ], - 'Sales Invoice': [ - dict(fieldname='invoice_copy', label='Invoice Copy', - fieldtype='Select', insert_after='project', print_hide=1, allow_on_submit=1, - options='ORIGINAL FOR RECIPIENT\nDUPLICATE FOR TRANSPORTER\nTRIPLICATE FOR SUPPLIER'), - ], - 'Sales Order Item': [hsn_sac_field], - 'Delivery Note Item': [hsn_sac_field], - 'Purchase Order Item': [hsn_sac_field], - 'Purchase Receipt Item': [hsn_sac_field] - } - - for doctype, fields in custom_fields.items(): - for df in fields: - create_custom_field(doctype, df) diff --git a/erpnext/patches/v8_1/remove_sales_invoice_from_returned_serial_no.py b/erpnext/patches/v8_1/remove_sales_invoice_from_returned_serial_no.py deleted file mode 100644 index 3962f8f1f2..0000000000 --- a/erpnext/patches/v8_1/remove_sales_invoice_from_returned_serial_no.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Serial No") - - frappe.db.sql(""" - update - `tabSerial No` - set - sales_invoice = NULL - where - sales_invoice in (select return_against from - `tabSales Invoice` where docstatus =1 and is_return=1) - and sales_invoice is not null and sales_invoice !='' """) \ No newline at end of file diff --git a/erpnext/patches/v8_1/removed_report_support_hours.py b/erpnext/patches/v8_1/removed_report_support_hours.py deleted file mode 100644 index 0936b2231b..0000000000 --- a/erpnext/patches/v8_1/removed_report_support_hours.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.db.sql(""" update `tabAuto Email Report` set report = %s - where name = %s""", ('Support Hour Distribution', 'Support Hours')) - - frappe.db.sql(""" update `tabCustom Role` set report = %s - where report = %s""", ('Support Hour Distribution', 'Support Hours')) - - frappe.delete_doc('Report', 'Support Hours') \ No newline at end of file diff --git a/erpnext/patches/v8_1/set_delivery_date_in_so_item.py b/erpnext/patches/v8_1/set_delivery_date_in_so_item.py deleted file mode 100644 index af2d28b857..0000000000 --- a/erpnext/patches/v8_1/set_delivery_date_in_so_item.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Sales Order") - frappe.reload_doctype("Sales Order Item") - - if "final_delivery_date" in frappe.db.get_table_columns("Sales Order"): - frappe.db.sql(""" - update `tabSales Order` - set delivery_date = final_delivery_date - where (delivery_date is null or delivery_date = '0000-00-00') - and order_type = 'Sales'""") - - frappe.db.sql(""" - update `tabSales Order` so, `tabSales Order Item` so_item - set so_item.delivery_date = so.delivery_date - where so.name = so_item.parent - and so.order_type = 'Sales' - and (so_item.delivery_date is null or so_item.delivery_date = '0000-00-00') - and (so.delivery_date is not null and so.delivery_date != '0000-00-00') - """) \ No newline at end of file diff --git a/erpnext/patches/v8_1/update_expense_claim_status.py b/erpnext/patches/v8_1/update_expense_claim_status.py deleted file mode 100644 index 4c1b85a13f..0000000000 --- a/erpnext/patches/v8_1/update_expense_claim_status.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype('Expense Claim') - - for data in frappe.db.sql(""" select name from `tabExpense Claim` - where (docstatus=1 and total_sanctioned_amount=0 and status = 'Paid') or - (docstatus = 1 and approval_status = 'Rejected' and total_sanctioned_amount > 0)""", as_dict=1): - doc = frappe.get_doc('Expense Claim', data.name) - if doc.approval_status == 'Rejected': - for d in doc.expenses: - d.db_set("sanctioned_amount", 0, update_modified = False) - doc.db_set("total_sanctioned_amount", 0, update_modified = False) - - frappe.db.sql(""" delete from `tabGL Entry` where voucher_type = 'Expense Claim' - and voucher_no = %s""", (doc.name)) - - doc.set_status() - doc.db_set("status", doc.status, update_modified = False) \ No newline at end of file diff --git a/erpnext/patches/v8_1/update_gst_state.py b/erpnext/patches/v8_1/update_gst_state.py deleted file mode 100644 index 7aaf2d5ff3..0000000000 --- a/erpnext/patches/v8_1/update_gst_state.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import unicode_literals -import frappe -from erpnext.regional.india import states - -def execute(): - company = frappe.get_all('Company', filters = {'country': 'India'}) - if not company: - return - - if not frappe.db.get_value("Custom Field", filters={'fieldname':'gst_state'}): - return - - frappe.db.sql("update `tabCustom Field` set options=%s where fieldname='gst_state'", '\n'.join(states)) - frappe.db.sql("update `tabAddress` set gst_state='Chhattisgarh' where gst_state='Chattisgarh'") - frappe.db.sql("update `tabAddress` set gst_state_number='05' where gst_state='Uttarakhand'") diff --git a/erpnext/patches/v8_10/__init__.py b/erpnext/patches/v8_10/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v8_10/change_default_customer_credit_days.py b/erpnext/patches/v8_10/change_default_customer_credit_days.py deleted file mode 100644 index 992be17da0..0000000000 --- a/erpnext/patches/v8_10/change_default_customer_credit_days.py +++ /dev/null @@ -1,89 +0,0 @@ -from __future__ import unicode_literals -import frappe - - -def execute(): - frappe.reload_doc("selling", "doctype", "customer") - frappe.reload_doc("buying", "doctype", "supplier") - frappe.reload_doc("setup", "doctype", "supplier_type") - frappe.reload_doc("accounts", "doctype", "payment_term") - frappe.reload_doc("accounts", "doctype", "payment_terms_template_detail") - frappe.reload_doc("accounts", "doctype", "payment_terms_template") - - payment_terms = [] - records = [] - for doctype in ("Customer", "Supplier", "Supplier Type"): - credit_days = frappe.db.sql(""" - SELECT DISTINCT `credit_days`, `credit_days_based_on`, `name` - from `tab{0}` - where - ((credit_days_based_on='Fixed Days' or credit_days_based_on is null) - and credit_days is not null) - or credit_days_based_on='Last Day of the Next Month' - """.format(doctype)) - - credit_records = ((record[0], record[1], record[2]) for record in credit_days) - for days, based_on, party_name in credit_records: - if based_on == "Fixed Days": - pyt_template_name = 'Default Payment Term - N{0}'.format(days) - else: - pyt_template_name = 'Default Payment Term - EO2M' - - if not frappe.db.exists("Payment Terms Template", pyt_template_name): - payment_term = make_payment_term(days, based_on) - template = make_template(payment_term) - else: - template = frappe.get_doc("Payment Terms Template", pyt_template_name) - - payment_terms.append('WHEN `name`={0} THEN {1}'.format(frappe.db.escape(party_name), template.template_name)) - records.append(frappe.db.escape(party_name)) - - begin_query_str = "UPDATE `tab{0}` SET `payment_terms` = CASE ".format(doctype) - value_query_str = " ".join(payment_terms) - cond_query_str = " ELSE `payment_terms` END WHERE " - - if records: - frappe.db.sql( - begin_query_str + value_query_str + cond_query_str + '`name` IN %s', - (records,) - ) - - -def make_template(payment_term): - doc = frappe.new_doc('Payment Terms Template Detail') - doc.payment_term = payment_term.payment_term_name - doc.due_date_based_on = payment_term.due_date_based_on - doc.invoice_portion = payment_term.invoice_portion - doc.description = payment_term.description - doc.credit_days = payment_term.credit_days - doc.credit_months = payment_term.credit_months - - template = frappe.new_doc('Payment Terms Template') - template.template_name = 'Default Payment Term - {0}'.format(payment_term.payment_term_name) - template.append('terms', doc) - template.save() - - return template - - -def make_payment_term(days, based_on): - based_on_map = { - 'Fixed Days': 'Day(s) after invoice date', - 'Last Day of the Next Month': 'Month(s) after the end of the invoice month' - } - - doc = frappe.new_doc('Payment Term') - doc.due_date_based_on = based_on_map.get(based_on) - doc.invoice_portion = 100 - - if based_on == 'Fixed Days': - doc.credit_days = days - doc.description = 'Net payable within {0} days'.format(days) - doc.payment_term_name = 'N{0}'.format(days) - else: - doc.credit_months = 1 - doc.description = 'Net payable by the end of next month' - doc.payment_term_name = 'EO2M' - - doc.save() - return doc diff --git a/erpnext/patches/v8_3/__init__.py b/erpnext/patches/v8_3/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v8_3/set_restrict_to_domain_for_module_def.py b/erpnext/patches/v8_3/set_restrict_to_domain_for_module_def.py deleted file mode 100644 index 6c4c6d5bd8..0000000000 --- a/erpnext/patches/v8_3/set_restrict_to_domain_for_module_def.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - """ set the restrict to domain in module def """ - pass \ No newline at end of file diff --git a/erpnext/patches/v8_3/update_company_total_sales.py b/erpnext/patches/v8_3/update_company_total_sales.py deleted file mode 100644 index 78efecb387..0000000000 --- a/erpnext/patches/v8_3/update_company_total_sales.py +++ /dev/null @@ -1,15 +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 erpnext.setup.doctype.company.company import update_company_current_month_sales, update_company_monthly_sales - -def execute(): - '''Update company monthly sales history based on sales invoices''' - frappe.reload_doctype("Company") - companies = [d['name'] for d in frappe.get_list("Company")] - - for company in companies: - update_company_current_month_sales(company) - update_company_monthly_sales(company) diff --git a/erpnext/patches/v8_4/__init__.py b/erpnext/patches/v8_4/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v8_4/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v8_4/make_scorecard_records.py b/erpnext/patches/v8_4/make_scorecard_records.py deleted file mode 100644 index 73afa277b4..0000000000 --- a/erpnext/patches/v8_4/make_scorecard_records.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import make_default_records -def execute(): - frappe.reload_doc('buying', 'doctype', 'supplier_scorecard_variable') - frappe.reload_doc('buying', 'doctype', 'supplier_scorecard_standing') - make_default_records() \ No newline at end of file diff --git a/erpnext/patches/v8_5/__init__.py b/erpnext/patches/v8_5/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v8_5/fix_tax_breakup_for_non_invoice_docs.py b/erpnext/patches/v8_5/fix_tax_breakup_for_non_invoice_docs.py deleted file mode 100644 index 82beba3770..0000000000 --- a/erpnext/patches/v8_5/fix_tax_breakup_for_non_invoice_docs.py +++ /dev/null @@ -1,48 +0,0 @@ -from __future__ import unicode_literals -import frappe -from erpnext.regional.india.setup import make_custom_fields -from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_html - -def execute(): - companies = [d.name for d in frappe.get_all('Company', filters = {'country': 'India'})] - if not companies: - return - - make_custom_fields() - - # update invoice copy value - values = ["Original for Recipient", "Duplicate for Transporter", - "Duplicate for Supplier", "Triplicate for Supplier"] - for d in values: - frappe.db.sql("update `tabSales Invoice` set invoice_copy=%s where invoice_copy=%s", (d, d)) - - # update tax breakup in transactions made after 1st July 2017 - doctypes = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", - "Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"] - - for doctype in doctypes: - frappe.reload_doctype(doctype) - - date_field = "posting_date" - if doctype in ["Quotation", "Sales Order", "Supplier Quotation", "Purchase Order"]: - date_field = "transaction_date" - - records = [d.name for d in frappe.get_all(doctype, filters={ - "docstatus": ["!=", 2], - date_field: [">=", "2017-07-01"], - "company": ["in", companies], - "total_taxes_and_charges": [">", 0], - "other_charges_calculation": "" - })] - if records: - frappe.db.sql(""" - update `tab%s Item` dt_item - set gst_hsn_code = (select gst_hsn_code from tabItem where name=dt_item.item_code) - where parent in (%s) - and (gst_hsn_code is null or gst_hsn_code = '') - """ % (doctype, ', '.join(['%s']*len(records))), tuple(records)) - - for record in records: - doc = frappe.get_doc(doctype, record) - html = get_itemised_tax_breakup_html(doc) - doc.db_set("other_charges_calculation", html, update_modified=False) \ No newline at end of file diff --git a/erpnext/patches/v8_5/remove_project_type_property_setter.py b/erpnext/patches/v8_5/remove_project_type_property_setter.py deleted file mode 100644 index 70a08f5377..0000000000 --- a/erpnext/patches/v8_5/remove_project_type_property_setter.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - ps = frappe.db.get_value('Property Setter', dict(doc_type='Project', field_name='project_type', - property='options')) - if ps: - frappe.delete_doc('Property Setter', ps) - - project_types = frappe.db.sql_list('select distinct project_type from tabProject') - - for project_type in project_types: - if project_type and not frappe.db.exists("Project Type", project_type): - p_type = frappe.get_doc({ - "doctype": "Project Type", - "project_type": project_type - }) - p_type.insert() \ No newline at end of file diff --git a/erpnext/patches/v8_5/remove_quotations_route_in_sidebar.py b/erpnext/patches/v8_5/remove_quotations_route_in_sidebar.py deleted file mode 100644 index 2d7df4a179..0000000000 --- a/erpnext/patches/v8_5/remove_quotations_route_in_sidebar.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Portal Settings") - - frappe.db.sql(""" - delete from - `tabPortal Menu Item` - where - (route = '/quotations' and title = 'Supplier Quotation') - or (route = '/quotation' and title = 'Quotations') - """) \ No newline at end of file diff --git a/erpnext/patches/v8_5/set_default_mode_of_payment.py b/erpnext/patches/v8_5/set_default_mode_of_payment.py deleted file mode 100644 index 34ecbb0a3c..0000000000 --- a/erpnext/patches/v8_5/set_default_mode_of_payment.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("POS Profile") - frappe.reload_doctype("Sales Invoice Payment") - - frappe.db.sql(""" - update - `tabSales Invoice Payment` - set `tabSales Invoice Payment`.default = 1 - where - `tabSales Invoice Payment`.parenttype = 'POS Profile' - and `tabSales Invoice Payment`.idx=1""") \ No newline at end of file diff --git a/erpnext/patches/v8_5/update_customer_group_in_POS_profile.py b/erpnext/patches/v8_5/update_customer_group_in_POS_profile.py deleted file mode 100644 index 2661914401..0000000000 --- a/erpnext/patches/v8_5/update_customer_group_in_POS_profile.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype('POS Profile') - customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group') - if customer_group: - frappe.db.sql(""" update `tabPOS Profile` - set customer_group = %s where customer_group is null """, (customer_group)) \ No newline at end of file diff --git a/erpnext/patches/v8_5/update_existing_data_in_project_type.py b/erpnext/patches/v8_5/update_existing_data_in_project_type.py deleted file mode 100644 index 497da0602e..0000000000 --- a/erpnext/patches/v8_5/update_existing_data_in_project_type.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("projects", "doctype", "project_type") - frappe.reload_doc("projects", "doctype", "project") - - project_types = ["Internal", "External", "Other"] - - for project_type in project_types: - if not frappe.db.exists("Project Type", project_type): - p_type = frappe.get_doc({ - "doctype": "Project Type", - "project_type": project_type - }) - p_type.insert() \ No newline at end of file diff --git a/erpnext/patches/v8_6/__init__.py b/erpnext/patches/v8_6/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v8_6/point_sms_doctype_module_to_frappe_core.py b/erpnext/patches/v8_6/point_sms_doctype_module_to_frappe_core.py deleted file mode 100644 index 014a74abe3..0000000000 --- a/erpnext/patches/v8_6/point_sms_doctype_module_to_frappe_core.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.db.sql('''UPDATE `tabDocType` SET module="Core" - WHERE name IN ("SMS Parameter", "SMS Settings");''') \ No newline at end of file diff --git a/erpnext/patches/v8_6/rename_bom_update_tool.py b/erpnext/patches/v8_6/rename_bom_update_tool.py deleted file mode 100644 index ef5f335e45..0000000000 --- a/erpnext/patches/v8_6/rename_bom_update_tool.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.delete_doc_if_exists("DocType", "BOM Replace Tool") - - frappe.reload_doctype("BOM") - frappe.db.sql("update tabBOM set conversion_rate=1 where conversion_rate is null or conversion_rate=0") - frappe.db.sql("update tabBOM set set_rate_of_sub_assembly_item_based_on_bom=1") \ No newline at end of file diff --git a/erpnext/patches/v8_6/set_write_permission_for_quotation_for_sales_manager.py b/erpnext/patches/v8_6/set_write_permission_for_quotation_for_sales_manager.py deleted file mode 100644 index db4f94748e..0000000000 --- a/erpnext/patches/v8_6/set_write_permission_for_quotation_for_sales_manager.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - # Set write permission to permlevel 1 for sales manager role in Quotation doctype - frappe.db.sql(""" update `tabCustom DocPerm` set `tabCustom DocPerm`.write = 1 - where `tabCustom DocPerm`.parent = 'Quotation' and `tabCustom DocPerm`.role = 'Sales Manager' - and `tabCustom DocPerm`.permlevel = 1 """) \ No newline at end of file diff --git a/erpnext/patches/v8_6/update_timesheet_company_from_PO.py b/erpnext/patches/v8_6/update_timesheet_company_from_PO.py deleted file mode 100644 index 2d46bee7ca..0000000000 --- a/erpnext/patches/v8_6/update_timesheet_company_from_PO.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype('Timesheet') - company = frappe.get_all('Company') - - #Check more than one company exists - if len(company) > 1: - frappe.db.sql(""" update `tabTimesheet` set `tabTimesheet`.company = - (select company from `tabWork Order` where name = `tabTimesheet`.work_order) - where workn_order is not null and work_order !=''""") \ No newline at end of file diff --git a/erpnext/patches/v8_7/__init__.py b/erpnext/patches/v8_7/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v8_7/fix_purchase_receipt_status.py b/erpnext/patches/v8_7/fix_purchase_receipt_status.py deleted file mode 100644 index 99ecb44214..0000000000 --- a/erpnext/patches/v8_7/fix_purchase_receipt_status.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - # there is no more status called "Submitted", there was an old issue that used - # to set it as Submitted, fixed in this commit - frappe.db.sql(""" - update - `tabPurchase Receipt` - set - status = 'To Bill' - where - status = 'Submitted'""") \ No newline at end of file diff --git a/erpnext/patches/v8_7/make_subscription_from_recurring_data.py b/erpnext/patches/v8_7/make_subscription_from_recurring_data.py deleted file mode 100644 index 2932749116..0000000000 --- a/erpnext/patches/v8_7/make_subscription_from_recurring_data.py +++ /dev/null @@ -1,58 +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 today - -def execute(): - frappe.reload_doc('accounts', 'doctype', 'subscription') - frappe.reload_doc('selling', 'doctype', 'sales_order') - frappe.reload_doc('selling', 'doctype', 'quotation') - frappe.reload_doc('buying', 'doctype', 'purchase_order') - frappe.reload_doc('buying', 'doctype', 'supplier_quotation') - frappe.reload_doc('accounts', 'doctype', 'sales_invoice') - frappe.reload_doc('accounts', 'doctype', 'purchase_invoice') - frappe.reload_doc('stock', 'doctype', 'purchase_receipt') - frappe.reload_doc('stock', 'doctype', 'delivery_note') - frappe.reload_doc('accounts', 'doctype', 'journal_entry') - frappe.reload_doc('accounts', 'doctype', 'payment_entry') - - for doctype in ['Sales Order', 'Sales Invoice', 'Purchase Order', 'Purchase Invoice']: - date_field = "transaction_date" - if doctype in ("Sales Invoice", "Purchase Invoice"): - date_field = "posting_date" - - for data in get_data(doctype, date_field): - make_subscription(doctype, data, date_field) - -def get_data(doctype, date_field): - return frappe.db.sql(""" select name, from_date, end_date, recurring_type, recurring_id, - next_date, notify_by_email, notification_email_address, recurring_print_format, - repeat_on_day_of_month, submit_on_creation, docstatus, {0} - from `tab{1}` where is_recurring = 1 and next_date >= %s and docstatus < 2 - order by next_date desc - """.format(date_field, doctype), today(), as_dict=1) - -def make_subscription(doctype, data, date_field): - if data.name == data.recurring_id: - start_date = data.get(date_field) - else: - start_date = frappe.db.get_value(doctype, data.recurring_id, date_field) - - doc = frappe.get_doc({ - 'doctype': 'Subscription', - 'reference_doctype': doctype, - 'reference_document': data.recurring_id, - 'start_date': start_date, - 'end_date': data.end_date, - 'frequency': data.recurring_type, - 'repeat_on_day': data.repeat_on_day_of_month, - 'notify_by_email': data.notify_by_email, - 'recipients': data.notification_email_address, - 'next_schedule_date': data.next_date, - 'submit_on_creation': data.submit_on_creation - }).insert(ignore_permissions=True) - - if data.docstatus == 1: - doc.submit() \ No newline at end of file diff --git a/erpnext/patches/v8_8/__init__.py b/erpnext/patches/v8_8/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v8_8/add_new_fields_in_accounts_settings.py b/erpnext/patches/v8_8/add_new_fields_in_accounts_settings.py deleted file mode 100644 index bd25f15d78..0000000000 --- a/erpnext/patches/v8_8/add_new_fields_in_accounts_settings.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals -import frappe - - -def execute(): - frappe.db.sql( - "INSERT INTO `tabSingles` (`doctype`, `field`, `value`) VALUES ('Accounts Settings', 'allow_stale', '1'), " - "('Accounts Settings', 'stale_days', '1')" - ) diff --git a/erpnext/patches/v8_8/set_bom_rate_as_per_uom.py b/erpnext/patches/v8_8/set_bom_rate_as_per_uom.py deleted file mode 100644 index 5b169cdff2..0000000000 --- a/erpnext/patches/v8_8/set_bom_rate_as_per_uom.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.db.sql(""" - update `tabBOM Item` - set rate = rate * conversion_factor - where uom != stock_uom and docstatus < 2 - and conversion_factor not in (0, 1) - """) \ No newline at end of file diff --git a/erpnext/patches/v8_9/__init__.py b/erpnext/patches/v8_9/__init__.py deleted file mode 100644 index 8b13789179..0000000000 --- a/erpnext/patches/v8_9/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/erpnext/patches/v8_9/add_setup_progress_actions.py b/erpnext/patches/v8_9/add_setup_progress_actions.py deleted file mode 100644 index 77501073cf..0000000000 --- a/erpnext/patches/v8_9/add_setup_progress_actions.py +++ /dev/null @@ -1,47 +0,0 @@ - -from __future__ import unicode_literals -import frappe -from frappe import _ - -def execute(): - """Add setup progress actions""" - if not frappe.db.exists('DocType', 'Setup Progress') or not frappe.db.exists('DocType', 'Setup Progress Action'): - return - - frappe.reload_doc("setup", "doctype", "setup_progress") - frappe.reload_doc("setup", "doctype", "setup_progress_action") - - actions = [ - {"action_name": "Add Company", "action_doctype": "Company", "min_doc_count": 1, "is_completed": 1, - "domains": '[]' }, - {"action_name": "Set Sales Target", "action_doctype": "Company", "min_doc_count": 99, - "action_document": frappe.defaults.get_defaults().get("company") or '', - "action_field": "monthly_sales_target", "is_completed": 0, - "domains": '["Manufacturing", "Services", "Retail", "Distribution"]' }, - {"action_name": "Add Customers", "action_doctype": "Customer", "min_doc_count": 1, "is_completed": 0, - "domains": '["Manufacturing", "Services", "Retail", "Distribution"]' }, - {"action_name": "Add Suppliers", "action_doctype": "Supplier", "min_doc_count": 1, "is_completed": 0, - "domains": '["Manufacturing", "Services", "Retail", "Distribution"]' }, - {"action_name": "Add Products", "action_doctype": "Item", "min_doc_count": 1, "is_completed": 0, - "domains": '["Manufacturing", "Services", "Retail", "Distribution"]' }, - {"action_name": "Add Programs", "action_doctype": "Program", "min_doc_count": 1, "is_completed": 0, - "domains": '["Education"]' }, - {"action_name": "Add Instructors", "action_doctype": "Instructor", "min_doc_count": 1, "is_completed": 0, - "domains": '["Education"]' }, - {"action_name": "Add Courses", "action_doctype": "Course", "min_doc_count": 1, "is_completed": 0, - "domains": '["Education"]' }, - {"action_name": "Add Rooms", "action_doctype": "Room", "min_doc_count": 1, "is_completed": 0, - "domains": '["Education"]' }, - {"action_name": "Add Users", "action_doctype": "User", "min_doc_count": 4, "is_completed": 0, - "domains": '[]' }, - {"action_name": "Add Letterhead", "action_doctype": "Letter Head", "min_doc_count": 1, "is_completed": 0, - "domains": '[]' } - ] - - setup_progress = frappe.get_doc("Setup Progress", "Setup Progress") - setup_progress.actions = [] - for action in actions: - setup_progress.append("actions", action) - - setup_progress.save(ignore_permissions=True) - diff --git a/erpnext/patches/v8_9/delete_gst_doctypes_for_outside_india_accounts.py b/erpnext/patches/v8_9/delete_gst_doctypes_for_outside_india_accounts.py deleted file mode 100644 index f67af90555..0000000000 --- a/erpnext/patches/v8_9/delete_gst_doctypes_for_outside_india_accounts.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - company = frappe.get_all('Company', filters = {'country': 'India'}) - if not company: - if frappe.db.exists("DocType", "GST Settings"): - frappe.delete_doc("DocType", "GST Settings") - frappe.delete_doc("DocType", "GST HSN Code") - - for report_name in ('GST Sales Register', 'GST Purchase Register', - 'GST Itemised Sales Register', 'GST Itemised Purchase Register'): - - frappe.delete_doc('Report', report_name) \ No newline at end of file diff --git a/erpnext/patches/v8_9/remove_employee_from_salary_structure_parent.py b/erpnext/patches/v8_9/remove_employee_from_salary_structure_parent.py deleted file mode 100644 index 808ae6d527..0000000000 --- a/erpnext/patches/v8_9/remove_employee_from_salary_structure_parent.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if 'employee' in frappe.db.get_table_columns("Salary Structure"): - frappe.db.sql("alter table `tabSalary Structure` drop column employee") diff --git a/erpnext/patches/v8_9/rename_company_sales_target_field.py b/erpnext/patches/v8_9/rename_company_sales_target_field.py deleted file mode 100644 index 5433eb673e..0000000000 --- a/erpnext/patches/v8_9/rename_company_sales_target_field.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import rename_field - -def execute(): - frappe.reload_doc("setup", "doctype", "company") - if frappe.db.has_column('Company', 'sales_target'): - rename_field("Company", "sales_target", "monthly_sales_target") diff --git a/erpnext/patches/v8_9/set_default_customer_group.py b/erpnext/patches/v8_9/set_default_customer_group.py deleted file mode 100644 index cbbe09daf5..0000000000 --- a/erpnext/patches/v8_9/set_default_customer_group.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - selling_settings = frappe.get_single('Selling Settings') - selling_settings.set_default_customer_group_and_territory() - selling_settings.flags.ignore_mandatory = True - selling_settings.save() diff --git a/erpnext/patches/v8_9/set_default_fields_in_variant_settings.py b/erpnext/patches/v8_9/set_default_fields_in_variant_settings.py deleted file mode 100644 index a550d093fa..0000000000 --- a/erpnext/patches/v8_9/set_default_fields_in_variant_settings.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2017, 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', 'item_variant_settings') - frappe.reload_doc('stock', 'doctype', 'variant_field') - - doc = frappe.get_doc('Item Variant Settings') - doc.set_default_fields() - doc.save() \ No newline at end of file diff --git a/erpnext/patches/v8_9/set_member_party_type.py b/erpnext/patches/v8_9/set_member_party_type.py deleted file mode 100644 index 33bbc11a93..0000000000 --- a/erpnext/patches/v8_9/set_member_party_type.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if not frappe.db.exists("Party Type", "Member"): - frappe.reload_doc("non_profit", "doctype", "member") - party = frappe.new_doc("Party Type") - party.party_type = "Member" - party.save() diff --git a/erpnext/patches/v8_9/set_print_zero_amount_taxes.py b/erpnext/patches/v8_9/set_print_zero_amount_taxes.py deleted file mode 100644 index 3c508eaa09..0000000000 --- a/erpnext/patches/v8_9/set_print_zero_amount_taxes.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals -import frappe - -from erpnext.setup.install import create_print_zero_amount_taxes_custom_field - -def execute(): - frappe.reload_doc('printing', 'doctype', 'print_style') - frappe.reload_doc('printing', 'doctype', 'print_settings') - create_print_zero_amount_taxes_custom_field() \ No newline at end of file diff --git a/erpnext/patches/v8_9/update_billing_gstin_for_indian_account.py b/erpnext/patches/v8_9/update_billing_gstin_for_indian_account.py deleted file mode 100644 index 24e20409c1..0000000000 --- a/erpnext/patches/v8_9/update_billing_gstin_for_indian_account.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - company = frappe.get_all('Company', filters = {'country': 'India'}) - - if company: - for doctype in ['Sales Invoice', 'Delivery Note']: - frappe.db.sql(""" update `tab{0}` - set billing_address_gstin = (select gstin from `tabAddress` - where name = customer_address) - where customer_address is not null and customer_address != ''""".format(doctype)) \ No newline at end of file diff --git a/erpnext/patches/v9_0/__init__.py b/erpnext/patches/v9_0/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/patches/v9_0/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/patches/v9_0/add_healthcare_domain.py b/erpnext/patches/v9_0/add_healthcare_domain.py deleted file mode 100644 index 3c0433b9d4..0000000000 --- a/erpnext/patches/v9_0/add_healthcare_domain.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - domain = 'Healthcare' - if not frappe.db.exists('Domain', domain): - frappe.get_doc({ - 'doctype': 'Domain', - 'domain': domain - }).insert(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/patches/v9_0/add_user_to_child_table_in_pos_profile.py b/erpnext/patches/v9_0/add_user_to_child_table_in_pos_profile.py deleted file mode 100644 index 8a8c8064dd..0000000000 --- a/erpnext/patches/v9_0/add_user_to_child_table_in_pos_profile.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.table_exists("POS Profile User"): - frappe.reload_doc('accounts', 'doctype', 'pos_profile_user') - - frappe.db.sql(""" update `tabPOS Profile User`, - (select `tabPOS Profile User`.name from `tabPOS Profile User`, `tabPOS Profile` - where `tabPOS Profile`.name = `tabPOS Profile User`.parent - group by `tabPOS Profile User`.user, `tabPOS Profile`.company) as pfu - set - `tabPOS Profile User`.default = 1 - where `tabPOS Profile User`.name = pfu.name""") - else: - doctype = 'POS Profile' - frappe.reload_doc('accounts', 'doctype', doctype) - frappe.reload_doc('accounts', 'doctype', 'pos_profile_user') - frappe.reload_doc('accounts', 'doctype', 'pos_item_group') - frappe.reload_doc('accounts', 'doctype', 'pos_customer_group') - - for doc in frappe.get_all(doctype): - _doc = frappe.get_doc(doctype, doc.name) - user = frappe.db.get_value(doctype, doc.name, 'user') - - if not user: continue - - _doc.append('applicable_for_users', { - 'user': user, - 'default': 1 - }) - - _doc.flags.ignore_validate = True - _doc.flags.ignore_mandatory = True - _doc.save() \ No newline at end of file diff --git a/erpnext/patches/v9_0/copy_old_fees_field_data.py b/erpnext/patches/v9_0/copy_old_fees_field_data.py deleted file mode 100644 index 14278209c7..0000000000 --- a/erpnext/patches/v9_0/copy_old_fees_field_data.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - # 'Schools' module changed to the 'Education' - # frappe.reload_doc("schools", "doctype", "fees") - frappe.reload_doc("education", "doctype", "fees") - - if "total_amount" not in frappe.db.get_table_columns("Fees"): - return - - frappe.db.sql("""update tabFees set grand_total=total_amount where grand_total = 0.0""") \ No newline at end of file diff --git a/erpnext/patches/v9_0/remove_non_existing_warehouse_from_stock_settings.py b/erpnext/patches/v9_0/remove_non_existing_warehouse_from_stock_settings.py deleted file mode 100644 index c685bbc681..0000000000 --- a/erpnext/patches/v9_0/remove_non_existing_warehouse_from_stock_settings.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - default_warehouse = frappe.db.get_value("Stock Settings", None, "default_warehouse") - if default_warehouse: - if not frappe.db.get_value("Warehouse", {"name": default_warehouse}): - frappe.db.set_value("Stock Settings", None, "default_warehouse", "") \ No newline at end of file diff --git a/erpnext/patches/v9_0/remove_subscription_module.py b/erpnext/patches/v9_0/remove_subscription_module.py deleted file mode 100644 index 493873f3e8..0000000000 --- a/erpnext/patches/v9_0/remove_subscription_module.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.exists('Module Def', 'Subscription'): - frappe.db.sql(""" delete from `tabModule Def` where name = 'Subscription'""") \ No newline at end of file diff --git a/erpnext/patches/v9_0/revert_manufacturing_user_role.py b/erpnext/patches/v9_0/revert_manufacturing_user_role.py deleted file mode 100644 index f38b7f29ce..0000000000 --- a/erpnext/patches/v9_0/revert_manufacturing_user_role.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if 'Manufacturing' in frappe.get_active_domains(): return - - role = 'Manufacturing User' - frappe.db.set_value('Role', role, 'restrict_to_domain', '') - frappe.db.set_value('Role', role, 'disabled', 0) - - users = frappe.get_all('Has Role', filters = { - 'parenttype': 'User', - 'role': ('in', ['System Manager', 'Manufacturing Manager']) - }, fields=['parent'], as_list=1) - - for user in users: - _user = frappe.get_doc('User', user[0]) - _user.append('roles', { - 'role': role - }) - _user.flags.ignore_validate = True - _user.save() diff --git a/erpnext/patches/v9_0/set_pos_profile_name.py b/erpnext/patches/v9_0/set_pos_profile_name.py deleted file mode 100644 index a3a9735215..0000000000 --- a/erpnext/patches/v9_0/set_pos_profile_name.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - doctype = 'POS Profile' - frappe.reload_doctype(doctype) - - for pos in frappe.get_all(doctype, filters={'disabled': 0}): - doc = frappe.get_doc(doctype, pos.name) - - if not doc.user: continue - - try: - pos_profile_name = doc.user + ' - ' + doc.company - doc.flags.ignore_validate = True - doc.flags.ignore_mandatory = True - doc.save() - - frappe.rename_doc(doctype, doc.name, pos_profile_name, force=True) - except frappe.LinkValidationError: - frappe.db.set_value("POS Profile", doc.name, 'disabled', 1) diff --git a/erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py b/erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py deleted file mode 100644 index 3d012978fa..0000000000 --- a/erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - for doctype in ("Material Request", "Purchase Order"): - frappe.reload_doctype(doctype) - frappe.reload_doctype(doctype + " Item") - - if not frappe.db.has_column(doctype, "schedule_date"): - continue - - #Update only submitted MR - for record in frappe.get_all(doctype, filters= [["docstatus", "=", 1]], fields=["name"]): - doc = frappe.get_doc(doctype, record) - if doc.items: - if not doc.schedule_date: - schedule_dates = [d.schedule_date for d in doc.items if d.schedule_date] - if len(schedule_dates) > 0: - min_schedule_date = min(schedule_dates) - frappe.db.set_value(doctype, record, - "schedule_date", min_schedule_date, update_modified=False) \ No newline at end of file diff --git a/erpnext/patches/v9_0/set_shipping_type_for_existing_shipping_rules.py b/erpnext/patches/v9_0/set_shipping_type_for_existing_shipping_rules.py deleted file mode 100644 index 5092695b7d..0000000000 --- a/erpnext/patches/v9_0/set_shipping_type_for_existing_shipping_rules.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Shipping Rule") - - # default "calculate_based_on" - frappe.db.sql('''update `tabShipping Rule` - set calculate_based_on = "Net Weight" - where ifnull(calculate_based_on, '') = '' ''') - - # default "shipping_rule_type" - frappe.db.sql('''update `tabShipping Rule` - set shipping_rule_type = "Selling" - where ifnull(shipping_rule_type, '') = '' ''') diff --git a/erpnext/patches/v9_0/set_uoms_in_variant_field.py b/erpnext/patches/v9_0/set_uoms_in_variant_field.py deleted file mode 100644 index 9e783d99be..0000000000 --- a/erpnext/patches/v9_0/set_uoms_in_variant_field.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import unicode_literals -import frappe - - -def execute(): - doc = frappe.get_doc('Item Variant Settings') - variant_field_names = [vf.field_name for vf in doc.fields] - if 'uoms' not in variant_field_names: - doc.append( - 'fields', { - 'field_name': 'uoms' - } - ) - doc.save() diff --git a/erpnext/patches/v9_0/set_variant_item_description.py b/erpnext/patches/v9_0/set_variant_item_description.py deleted file mode 100644 index 82d6148508..0000000000 --- a/erpnext/patches/v9_0/set_variant_item_description.py +++ /dev/null @@ -1,46 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.utils import cstr - -def execute(): - ''' - Issue: - While copying data from template item to variant item, - the system appending description multiple times to the respective variant. - - Purpose: - Check variant description, - if variant have user defined description remove all system appended descriptions - else replace multiple system generated descriptions with single description - - Steps: - 1. Get all variant items - 2. Create system generated variant description - 3. If variant have user defined description, remove all system generated descriptions - 4. If variant description only contains system generated description, - replace multiple descriptions by new description. - ''' - for item in frappe.db.sql(""" select name from tabItem - where ifnull(variant_of, '') != '' """,as_dict=1): - variant = frappe.get_doc("Item", item.name) - temp_variant_description = '\n' - - if variant.attributes: - for d in variant.attributes: - temp_variant_description += "
" + d.attribute + ": " + cstr(d.attribute_value) + "
" - - variant_description = variant.description.replace(temp_variant_description, '').rstrip() - if variant_description: - splitted_desc = variant.description.strip().split(temp_variant_description) - - if len(splitted_desc) > 2: - if splitted_desc[0] == '': - variant_description = temp_variant_description + variant_description - elif splitted_desc[1] == '' or splitted_desc[1] == '\n': - variant_description += temp_variant_description - variant.db_set('description', variant_description, update_modified=False) - else: - variant.db_set('description', variant_description, update_modified=False) - - else: - variant.db_set('description', temp_variant_description, update_modified=False) \ No newline at end of file diff --git a/erpnext/patches/v9_0/student_admission_childtable_migrate.py b/erpnext/patches/v9_0/student_admission_childtable_migrate.py deleted file mode 100644 index a5712c76dc..0000000000 --- a/erpnext/patches/v9_0/student_admission_childtable_migrate.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - # 'Schools' module changed to the 'Education' - # frappe.reload_doc('schools', 'doctype', 'Student Admission Program') - # frappe.reload_doc('schools', 'doctype', 'student_admission') - frappe.reload_doc('education', 'doctype', 'Student Admission Program') - frappe.reload_doc('education', 'doctype', 'student_admission') - - if "program" not in frappe.db.get_table_columns("Student Admission"): - return - - student_admissions = frappe.get_all("Student Admission", fields=["name", "application_fee", \ - "naming_series_for_student_applicant", "program", "introduction", "eligibility"]) - for student_admission in student_admissions: - doc = frappe.get_doc("Student Admission", student_admission.name) - doc.append("program_details", { - "program": student_admission.get("program"), - "application_fee": student_admission.get("application_fee"), - "applicant_naming_series": student_admission.get("naming_series_for_student_applicant"), - }) - if student_admission.eligibility and student_admission.introduction: - doc.introduction = student_admission.introduction + "
" + \ - student_admission.eligibility + "
" - doc.flags.ignore_validate = True - doc.flags.ignore_mandatory = True - doc.save() diff --git a/erpnext/patches/v9_0/update_employee_loan_details.py b/erpnext/patches/v9_0/update_employee_loan_details.py deleted file mode 100644 index ef8d32855f..0000000000 --- a/erpnext/patches/v9_0/update_employee_loan_details.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('Payroll', 'doctype', 'salary_slip_loan') - frappe.reload_doc('Payroll', 'doctype', 'salary_slip') - - for data in frappe.db.sql(""" select name, - start_date, end_date, total_loan_repayment - from - `tabSalary Slip` - where - docstatus < 2 and ifnull(total_loan_repayment, 0) > 0""", as_dict=1): - salary_slip = frappe.get_doc('Salary Slip', data.name) - salary_slip.set_loan_repayment() - - if salary_slip.total_loan_repayment == data.total_loan_repayment: - for row in salary_slip.loans: - row.db_update() - - salary_slip.db_update() diff --git a/erpnext/patches/v9_0/update_multi_uom_fields_in_material_request.py b/erpnext/patches/v9_0/update_multi_uom_fields_in_material_request.py deleted file mode 100644 index 45610ed5a7..0000000000 --- a/erpnext/patches/v9_0/update_multi_uom_fields_in_material_request.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype('Material Request') - frappe.reload_doctype('Material Request Item') - - frappe.db.sql(""" update `tabMaterial Request Item` - set stock_uom = uom, stock_qty = qty, conversion_factor = 1.0""") \ No newline at end of file diff --git a/erpnext/patches/v9_1/__init__.py b/erpnext/patches/v9_1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v9_1/create_issue_opportunity_type.py b/erpnext/patches/v9_1/create_issue_opportunity_type.py deleted file mode 100644 index aa8bbd1e79..0000000000 --- a/erpnext/patches/v9_1/create_issue_opportunity_type.py +++ /dev/null @@ -1,34 +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 import _ - -def execute(): - # delete custom field if exists - for doctype, fieldname in (('Issue', 'issue_type'), ('Opportunity', 'opportunity_type')): - custom_field = frappe.db.get_value("Custom Field", {"fieldname": fieldname, 'dt': doctype}) - if custom_field: - frappe.delete_doc("Custom Field", custom_field, ignore_permissions=True) - - frappe.reload_doc('support', 'doctype', 'issue_type') - frappe.reload_doc('support', 'doctype', 'issue') - frappe.reload_doc('crm', 'doctype', 'opportunity_type') - frappe.reload_doc('crm', 'doctype', 'opportunity') - - # rename enquiry_type -> opportunity_type - from frappe.model.utils.rename_field import rename_field - rename_field('Opportunity', 'enquiry_type', 'opportunity_type') - - # create values if already set - for opts in (('Issue', 'issue_type', 'Issue Type'), - ('Opportunity', 'opportunity_type', 'Opportunity Type')): - for d in frappe.db.sql('select distinct {0} from `tab{1}`'.format(opts[1], opts[0])): - if d[0] and not frappe.db.exists(opts[2], d[0]): - frappe.get_doc(dict(doctype = opts[2], name=d[0])).insert() - - # fixtures - for name in ('Hub', _('Sales'), _('Support'), _('Maintenance')): - if not frappe.db.exists('Opportunity Type', name): - frappe.get_doc(dict(doctype = 'Opportunity Type', name=name)).insert() diff --git a/erpnext/patches/v9_2/__init__.py b/erpnext/patches/v9_2/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/patches/v9_2/delete_healthcare_domain_default_items.py b/erpnext/patches/v9_2/delete_healthcare_domain_default_items.py deleted file mode 100644 index 54ae18b8e2..0000000000 --- a/erpnext/patches/v9_2/delete_healthcare_domain_default_items.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.utils import getdate - -def execute(): - domain_settings = frappe.get_doc('Domain Settings') - active_domains = [d.domain for d in domain_settings.active_domains] - - if "Healthcare" not in active_domains: - items = ["TTT", "MCH", "LDL", "GTT", "HDL", "BILT", "BILD", "BP", "BS"] - for item_code in items: - try: - item = frappe.db.get_value("Item", {"item_code": item_code}, ["name", "creation"], as_dict=1) - if item and getdate(item.creation) >= getdate("2017-11-10"): - frappe.delete_doc("Item", item.name) - except: - pass \ No newline at end of file diff --git a/erpnext/patches/v9_2/delete_process_payroll.py b/erpnext/patches/v9_2/delete_process_payroll.py deleted file mode 100644 index 91c49f577f..0000000000 --- a/erpnext/patches/v9_2/delete_process_payroll.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.delete_doc("DocType", "Process Payroll") diff --git a/erpnext/patches/v9_2/remove_company_from_patient.py b/erpnext/patches/v9_2/remove_company_from_patient.py deleted file mode 100644 index 1a50088f23..0000000000 --- a/erpnext/patches/v9_2/remove_company_from_patient.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - if frappe.db.exists("DocType", "Patient"): - if 'company' in frappe.db.get_table_columns("Patient"): - frappe.db.sql("alter table `tabPatient` drop column company") diff --git a/erpnext/patches/v9_2/rename_net_weight_in_item_master.py b/erpnext/patches/v9_2/rename_net_weight_in_item_master.py deleted file mode 100644 index cad979deab..0000000000 --- a/erpnext/patches/v9_2/rename_net_weight_in_item_master.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.model.utils.rename_field import rename_field - -def execute(): - frappe.reload_doc("stock", "doctype", "item") - if frappe.db.has_column('Item', 'net_weight'): - rename_field("Item", "net_weight", "weight_per_unit") diff --git a/erpnext/patches/v9_2/rename_translated_domains_in_en.py b/erpnext/patches/v9_2/rename_translated_domains_in_en.py deleted file mode 100644 index e5a9e2461f..0000000000 --- a/erpnext/patches/v9_2/rename_translated_domains_in_en.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.model.rename_doc import rename_doc - -def execute(): - frappe.reload_doc('stock', 'doctype', 'item') - language = frappe.get_single("System Settings").language - - if language and language.startswith('en'): return - - frappe.local.lang = language - - all_domains = frappe.get_hooks("domains") - - for domain in all_domains: - translated_domain = _(domain, lang=language) - if frappe.db.exists("Domain", translated_domain): - #if domain already exists merged translated_domain and domain - merge = False - if frappe.db.exists("Domain", domain): - merge=True - - rename_doc("Domain", translated_domain, domain, ignore_permissions=True, merge=merge) - - domain_settings = frappe.get_single("Domain Settings") - active_domains = [d.domain for d in domain_settings.active_domains] - - try: - for domain in active_domains: - domain = frappe.get_doc("Domain", domain) - domain.setup_domain() - - if int(frappe.db.get_single_value('System Settings', 'setup_complete')): - domain.setup_sidebar_items() - domain.setup_desktop_icons() - domain.set_default_portal_role() - except frappe.LinkValidationError: - pass \ No newline at end of file diff --git a/erpnext/patches/v9_2/repost_reserved_qty_for_production.py b/erpnext/patches/v9_2/repost_reserved_qty_for_production.py deleted file mode 100644 index 040e655bd8..0000000000 --- a/erpnext/patches/v9_2/repost_reserved_qty_for_production.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc("stock", "doctype", "bin") - bins = frappe.db.sql("select name from `tabBin` where reserved_qty_for_production > 0") - for d in bins: - bin_doc = frappe.get_doc("Bin", d[0]) - bin_doc.update_reserved_qty_for_production() diff --git a/erpnext/patches/v9_2/set_item_name_in_production_order.py b/erpnext/patches/v9_2/set_item_name_in_production_order.py deleted file mode 100644 index 1f490e62c8..0000000000 --- a/erpnext/patches/v9_2/set_item_name_in_production_order.py +++ /dev/null @@ -1,12 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - - frappe.db.sql(""" - update `tabBOM Item` bom, `tabWork Order Item` po_item - set po_item.item_name = bom.item_name, - po_item.description = bom.description - where po_item.item_code = bom.item_code - and (po_item.item_name is null or po_item.description is null) - """) From 3dfbfe87e9e67a3605cf6f92c05a63455fd1bff1 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Jun 2021 11:47:09 +0530 Subject: [PATCH 120/180] chore: Drop < v10 patches from list v7 backup was restored and upgraded to latest v10.x.x branch. The patches run uptil the upgrade are removed in this change. This means only existing v10 sites are allowed direct upgrade to v13 and newer There are older version patches still left since they're being used in later ERPNext versions too. --- erpnext/patches.txt | 495 -------------------------------------------- 1 file changed, 495 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 93689a0ef3..ed6fefdd87 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -1,494 +1,19 @@ -execute:import unidecode # new requirement -erpnext.patches.v8_0.move_perpetual_inventory_setting -erpnext.patches.v8_9.set_print_zero_amount_taxes erpnext.patches.v12_0.update_is_cancelled_field erpnext.patches.v11_0.rename_production_order_to_work_order erpnext.patches.v11_0.refactor_naming_series erpnext.patches.v11_0.refactor_autoname_naming -erpnext.patches.v10_0.rename_schools_to_education -erpnext.patches.v4_0.validate_v3_patch -erpnext.patches.v4_0.fix_employee_user_id -erpnext.patches.v4_0.remove_employee_role_if_no_employee -erpnext.patches.v4_0.update_user_properties -erpnext.patches.v4_0.apply_user_permissions -erpnext.patches.v4_0.move_warehouse_user_to_restrictions -erpnext.patches.v4_0.global_defaults_to_system_settings -erpnext.patches.v4_0.update_incharge_name_to_sales_person_in_maintenance_schedule execute:frappe.reload_doc("accounts", "doctype", "POS Payment Method") #2020-05-28 execute:frappe.reload_doc("HR", "doctype", "HR Settings") #2020-01-16 #2020-07-24 -execute:frappe.reload_doc('stock', 'doctype', 'warehouse') # 2017-04-24 -execute:frappe.reload_doc('accounts', 'doctype', 'sales_invoice') # 2016-08-31 -execute:frappe.reload_doc('selling', 'doctype', 'sales_order') # 2014-01-29 -execute:frappe.reload_doc('selling', 'doctype', 'quotation') # 2014-01-29 -execute:frappe.reload_doc('stock', 'doctype', 'delivery_note') # 2014-01-29 -erpnext.patches.v4_0.reload_sales_print_format -execute:frappe.reload_doc('accounts', 'doctype', 'purchase_invoice') # 2014-01-29 -execute:frappe.reload_doc('buying', 'doctype', 'purchase_order') # 2014-01-29 -execute:frappe.reload_doc('buying', 'doctype', 'supplier_quotation') # 2014-01-29 -execute:frappe.reload_doc('stock', 'doctype', 'purchase_receipt') # 2014-01-29 -execute:frappe.reload_doc('accounts', 'doctype', 'pos_setting') # 2014-01-29 -execute:frappe.reload_doc('selling', 'doctype', 'customer') # 2014-01-29 -execute:frappe.reload_doc('buying', 'doctype', 'supplier') # 2014-01-29 -execute:frappe.reload_doc('accounts', 'doctype', 'asset_category') -execute:frappe.reload_doc('accounts', 'doctype', 'pricing_rule') -erpnext.patches.v4_0.map_charge_to_taxes_and_charges -execute:frappe.reload_doc('support', 'doctype', 'newsletter') # 2014-01-31 -execute:frappe.reload_doc('hr', 'doctype', 'employee') # 2014-02-03 -execute:frappe.db.sql("update tabPage set module='Core' where name='Setup'") -erpnext.patches.v5_2.change_item_selects_to_checks -execute:frappe.reload_doctype('Item') -erpnext.patches.v4_0.fields_to_be_renamed -erpnext.patches.v4_0.rename_sitemap_to_route -erpnext.patches.v7_0.re_route #2016-06-27 -erpnext.patches.v4_0.fix_contact_address -erpnext.patches.v4_0.customer_discount_to_pricing_rule -execute:frappe.db.sql("""delete from `tabWebsite Item Group` where ifnull(item_group, '')=''""") -erpnext.patches.v4_0.remove_module_home_pages -erpnext.patches.v4_0.split_email_settings -erpnext.patches.v4_0.import_country_codes -erpnext.patches.v4_0.countrywise_coa -execute:frappe.delete_doc("DocType", "MIS Control") -execute:frappe.delete_doc("Page", "Financial Statements") -execute:frappe.delete_doc("DocType", "Stock Ledger") -execute:frappe.delete_doc("DocType", "Grade") -execute:frappe.db.sql("delete from `tabWebsite Item Group` where ifnull(item_group, '')=''") -execute:frappe.delete_doc("Print Format", "SalesInvoice") -execute:import frappe.defaults;frappe.defaults.clear_default("price_list_currency") -erpnext.patches.v4_0.update_account_root_type -execute:frappe.delete_doc("Report", "Purchase In Transit") -erpnext.patches.v4_0.new_address_template -execute:frappe.delete_doc("DocType", "SMS Control") -execute:frappe.delete_doc_if_exists("DocType", "Bulk SMS") #2015-08-18 -erpnext.patches.v4_0.fix_case_of_hr_module_def -erpnext.patches.v4_0.fix_address_template - -# WATCHOUT: This patch reload's documents -erpnext.patches.v4_0.reset_permissions_for_masters -erpnext.patches.v6_20x.rename_project_name_to_project #2016-03-14 - -erpnext.patches.v4_0.update_tax_amount_after_discount -execute:frappe.permissions.reset_perms("GL Entry") #2014-06-09 -execute:frappe.permissions.reset_perms("Stock Ledger Entry") #2014-06-09 -erpnext.patches.v4_0.create_custom_fields_for_india_specific_fields -erpnext.patches.v4_0.save_default_letterhead -erpnext.patches.v4_0.update_custom_print_formats_for_renamed_fields -erpnext.patches.v4_0.update_other_charges_in_custom_purchase_print_formats -erpnext.patches.v4_0.create_price_list_if_missing -execute:frappe.db.sql("update `tabItem` set end_of_life=null where end_of_life='0000-00-00'") #2014-06-16 -erpnext.patches.v4_0.update_users_report_view_settings -erpnext.patches.v4_0.set_pricing_rule_for_buying_or_selling -erpnext.patches.v4_1.set_outgoing_email_footer -erpnext.patches.v4_1.fix_sales_order_delivered_status -erpnext.patches.v4_1.fix_delivery_and_billing_status -execute:frappe.db.sql("update `tabAccount` set root_type='Liability' where root_type='Income' and report_type='Balance Sheet'") -execute:frappe.delete_doc("DocType", "Payment to Invoice Matching Tool") # 29-07-2014 -execute:frappe.delete_doc("DocType", "Payment to Invoice Matching Tool Detail") # 29-07-2014 -execute:frappe.delete_doc("Page", "trial-balance") #2014-07-22 -erpnext.patches.v4_2.delete_old_print_formats #2014-07-29 -erpnext.patches.v4_2.toggle_rounded_total #2014-07-30 -erpnext.patches.v4_2.fix_account_master_type -erpnext.patches.v4_2.update_project_milestones -erpnext.patches.v4_2.add_currency_turkish_lira #2014-08-08 -execute:frappe.delete_doc("DocType", "Landed Cost Wizard") -erpnext.patches.v4_2.default_website_style -erpnext.patches.v4_2.set_company_country -erpnext.patches.v4_2.update_sales_order_invoice_field_name -erpnext.patches.v4_2.seprate_manufacture_and_repack -execute:frappe.delete_doc("Report", "Warehouse-Wise Stock Balance") -execute:frappe.delete_doc("DocType", "Purchase Request") -execute:frappe.delete_doc("DocType", "Purchase Request Item") -erpnext.patches.v4_2.recalculate_bom_cost -erpnext.patches.v4_2.fix_gl_entries_for_stock_transactions erpnext.patches.v4_2.update_requested_and_ordered_qty #2021-03-31 -execute:frappe.rename_doc("DocType", "Support Ticket", "Issue", force=True) -erpnext.patches.v4_4.make_email_accounts -execute:frappe.delete_doc("DocType", "Contact Control") -erpnext.patches.v4_2.discount_amount -erpnext.patches.v4_2.reset_bom_costs -erpnext.patches.v5_0.update_frozen_accounts_permission_role -erpnext.patches.v5_0.update_dn_against_doc_fields -execute:frappe.db.sql("update `tabMaterial Request` set material_request_type = 'Material Transfer' where material_request_type = 'Transfer'") -execute:frappe.reload_doc('stock', 'doctype', 'item') -erpnext.patches.v5_0.set_default_company_in_bom -execute:frappe.reload_doc('crm', 'doctype', 'lead') -execute:frappe.reload_doc('crm', 'doctype', 'opportunity') -erpnext.patches.v5_0.rename_taxes_and_charges_master -erpnext.patches.v5_1.sales_bom_rename -erpnext.patches.v5_0.rename_table_fieldnames -execute:frappe.db.sql("update `tabJournal Entry` set voucher_type='Journal Entry' where ifnull(voucher_type, '')=''") -erpnext.patches.v5_0.is_group -erpnext.patches.v4_2.party_model -erpnext.patches.v5_0.party_model_patch_fix -erpnext.patches.v4_1.fix_jv_remarks -erpnext.patches.v4_2.update_landed_cost_voucher -erpnext.patches.v4_2.set_item_has_batch -erpnext.patches.v4_2.update_stock_uom_for_dn_in_sle -erpnext.patches.v5_0.recalculate_total_amount_in_jv -erpnext.patches.v5_0.update_companywise_payment_account -erpnext.patches.v5_0.remove_birthday_events -erpnext.patches.v5_0.update_item_name_in_bom -erpnext.patches.v5_0.rename_customer_issue -erpnext.patches.v5_0.rename_total_fields -erpnext.patches.v5_0.new_crm_module -erpnext.patches.v5_0.rename_customer_issue -erpnext.patches.v5_0.update_material_transfer_for_manufacture -execute:frappe.reload_doc('crm', 'doctype', 'opportunity_item') -erpnext.patches.v5_0.update_item_description_and_image -erpnext.patches.v5_0.update_material_transferred_for_manufacturing -erpnext.patches.v5_0.stock_entry_update_value -erpnext.patches.v5_0.convert_stock_reconciliation -erpnext.patches.v5_0.update_projects -erpnext.patches.v5_0.item_patches -erpnext.patches.v5_0.update_journal_entry_title -erpnext.patches.v5_0.taxes_and_totals_in_party_currency -erpnext.patches.v5_0.replace_renamed_fields_in_custom_scripts_and_print_formats -erpnext.patches.v5_0.update_from_bom -erpnext.patches.v5_0.update_account_types -erpnext.patches.v5_0.update_sms_sender -erpnext.patches.v5_0.set_appraisal_remarks -erpnext.patches.v5_0.update_time_log_title -erpnext.patches.v7_0.create_warehouse_nestedset -erpnext.patches.v7_0.merge_account_type_stock_and_warehouse_to_stock -erpnext.patches.v7_0.set_is_group_for_warehouse -erpnext.patches.v7_2.stock_uom_in_selling -erpnext.patches.v4_2.repost_sle_for_si_with_no_warehouse -erpnext.patches.v5_0.newsletter -execute:frappe.delete_doc("DocType", "Chart of Accounts") -execute:frappe.delete_doc("DocType", "Style Settings") -erpnext.patches.v5_0.update_opportunity -erpnext.patches.v5_0.opportunity_not_submittable -execute:frappe.permissions.reset_perms("Purchase Taxes and Charges Template") #2014-06-09 -execute:frappe.permissions.reset_perms("Expense Claim Type") #2014-06-19 -erpnext.patches.v5_0.execute_on_doctype_update -erpnext.patches.v4_2.fix_recurring_orders -erpnext.patches.v4_2.delete_gl_entries_for_cancelled_invoices -erpnext.patches.v5_0.project_costing -erpnext.patches.v5_0.update_temporary_account -erpnext.patches.v5_0.update_advance_paid -erpnext.patches.v5_0.link_warehouse_with_account -execute:frappe.delete_doc("Page", "stock-ledger") -execute:frappe.delete_doc("Page","stock-level") -erpnext.patches.v5_0.reclculate_planned_operating_cost_in_production_order -erpnext.patches.v5_0.repost_requested_qty -erpnext.patches.v5_0.fix_taxes_and_totals_in_party_currency -erpnext.patches.v5_0.update_tax_amount_after_discount_in_purchase_cycle -erpnext.patches.v5_0.rename_pos_setting -erpnext.patches.v5_0.update_operation_description -erpnext.patches.v5_0.set_footer_address -execute:frappe.db.set_value("Backup Manager", None, "send_backups_to_dropbox", 1 if frappe.db.get_value("Backup Manager", None, "upload_backups_to_dropbox") in ("Daily", "Weekly") else 0) -execute:frappe.db.sql_list("delete from `tabDocPerm` where parent='Issue' and modified_by='Administrator' and role='Guest'") -erpnext.patches.v5_0.update_item_and_description_again -erpnext.patches.v6_0.multi_currency -erpnext.patches.v7_0.create_budget_record -erpnext.patches.v5_0.repost_gle_for_jv_with_multiple_party -erpnext.patches.v5_0.portal_fixes -erpnext.patches.v5_0.reset_values_in_tools # 02-05-2016 -execute:frappe.delete_doc("Page", "users") -erpnext.patches.v5_0.update_material_transferred_for_manufacturing_again -erpnext.patches.v5_0.index_on_account_and_gl_entry -execute:frappe.db.sql("""delete from `tabProject Task`""") -erpnext.patches.v5_0.update_item_desc_in_invoice -erpnext.patches.v5_1.fix_against_account -execute:frappe.rename_doc("DocType", "Salary Manager", "Process Payroll", force=True) -erpnext.patches.v5_1.rename_roles -erpnext.patches.v5_1.default_bom -execute:frappe.delete_doc("DocType", "Party Type") -execute:frappe.delete_doc("Module Def", "Contacts") -erpnext.patches.v5_4.fix_reserved_qty_and_sle_for_packed_items # 30-07-2015 -execute:frappe.reload_doctype("Leave Type") -execute:frappe.db.sql("update `tabLeave Type` set include_holiday=0") -erpnext.patches.v5_4.set_root_and_report_type -erpnext.patches.v5_4.notify_system_managers_regarding_wrong_tax_calculation -erpnext.patches.v5_4.fix_invoice_outstanding -execute:frappe.db.sql("update `tabStock Ledger Entry` set stock_queue = '[]' where voucher_type = 'Stock Reconciliation' and ifnull(qty_after_transaction, 0) = 0") -erpnext.patches.v5_4.fix_missing_item_images -erpnext.patches.v5_4.stock_entry_additional_costs -erpnext.patches.v5_4.cleanup_journal_entry #2015-08-14 erpnext.patches.v5_7.update_item_description_based_on_item_master -erpnext.patches.v5_7.item_template_attributes -execute:frappe.delete_doc_if_exists("DocType", "Manage Variants") -execute:frappe.delete_doc_if_exists("DocType", "Manage Variants Item") erpnext.patches.v4_2.repost_reserved_qty #2021-03-31 -erpnext.patches.v5_4.update_purchase_cost_against_project -erpnext.patches.v5_8.update_order_reference_in_return_entries -erpnext.patches.v5_8.add_credit_note_print_heading -execute:frappe.delete_doc_if_exists("Print Format", "Credit Note - Negative Invoice") - -# V6.0 -erpnext.patches.v6_0.set_default_title # 2015-09-03 -erpnext.patches.v6_0.default_activity_rate -execute:frappe.db.set_value("Stock Settings", None, "automatically_set_serial_nos_based_on_fifo", 1) -execute:frappe.db.sql("""update `tabProject` set percent_complete=round(percent_complete, 2) where percent_complete is not null""") -erpnext.patches.v6_0.fix_outstanding_amount -erpnext.patches.v6_0.fix_planned_qty -erpnext.patches.v6_2.remove_newsletter_duplicates -erpnext.patches.v6_2.fix_missing_default_taxes_and_lead -erpnext.patches.v6_3.convert_applicable_territory -erpnext.patches.v6_4.round_status_updater_percentages -erpnext.patches.v6_4.repost_gle_for_journal_entries_where_reference_name_missing -erpnext.patches.v6_4.fix_journal_entries_due_to_reconciliation -erpnext.patches.v6_4.fix_status_in_sales_and_purchase_order -erpnext.patches.v6_4.fix_modified_in_sales_order_and_purchase_order -erpnext.patches.v6_4.fix_duplicate_bins -erpnext.patches.v6_4.fix_sales_order_maintenance_status -erpnext.patches.v6_4.email_digest_update - -# delete shopping cart doctypes -execute:frappe.delete_doc_if_exists("DocType", "Applicable Territory") -execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Price List") -execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Taxes and Charges Master") - -erpnext.patches.v6_4.set_user_in_contact -erpnext.patches.v6_4.make_image_thumbnail #2015-10-20 -erpnext.patches.v6_5.show_in_website_for_template_item -erpnext.patches.v6_4.fix_expense_included_in_valuation -execute:frappe.delete_doc_if_exists("Report", "Item-wise Last Purchase Rate") -erpnext.patches.v6_6.fix_website_image -erpnext.patches.v6_6.remove_fiscal_year_from_leave_allocation -execute:frappe.delete_doc_if_exists("DocType", "Stock UOM Replace Utility") -erpnext.patches.v6_8.make_webform_standard #2015-11-23 -erpnext.patches.v6_8.move_drop_ship_to_po_items -erpnext.patches.v6_10.fix_ordered_received_billed -erpnext.patches.v6_10.fix_jv_total_amount #2015-11-30 -erpnext.patches.v6_10.email_digest_default_quote -erpnext.patches.v6_10.fix_billed_amount_in_drop_ship_po -erpnext.patches.v6_10.fix_delivery_status_of_drop_ship_item #2015-12-08 -erpnext.patches.v5_8.tax_rule #2015-12-08 -erpnext.patches.v6_12.set_overdue_tasks -erpnext.patches.v6_16.update_billing_status_in_dn_and_pr -erpnext.patches.v6_16.create_manufacturer_records -execute:frappe.db.sql("update `tabPricing Rule` set title=name where title='' or title is null") #2016-01-27 -erpnext.patches.v6_20.set_party_account_currency_in_orders -erpnext.patches.v6_19.comment_feed_communication -erpnext.patches.v6_21.fix_reorder_level -erpnext.patches.v6_21.rename_material_request_fields -erpnext.patches.v6_23.update_stopped_status_to_closed -erpnext.patches.v6_24.set_recurring_id -erpnext.patches.v6_20x.set_compact_print -execute:frappe.delete_doc_if_exists("Web Form", "contact") #2016-03-10 -erpnext.patches.v6_20x.remove_fiscal_year_from_holiday_list -erpnext.patches.v6_24.map_customer_address_to_shipping_address_on_po -erpnext.patches.v6_27.fix_recurring_order_status -erpnext.patches.v6_20x.update_product_bundle_description -erpnext.patches.v7_0.update_party_status #2016-09-22 -erpnext.patches.v7_0.remove_features_setup -erpnext.patches.v7_0.update_home_page -execute:frappe.delete_doc_if_exists("Page", "financial-analytics") -erpnext.patches.v7_0.update_project_in_gl_entry -execute:frappe.db.sql('update tabQuotation set status="Cancelled" where docstatus=2') -execute:frappe.rename_doc("DocType", "Payments", "Sales Invoice Payment", force=True) -erpnext.patches.v7_0.update_mins_to_first_response -erpnext.patches.v6_20x.repost_valuation_rate_for_negative_inventory -erpnext.patches.v7_0.migrate_mode_of_payments_v6_to_v7 -erpnext.patches.v7_0.system_settings_setup_complete -erpnext.patches.v7_0.set_naming_series_for_timesheet #2016-07-27 -execute:frappe.reload_doc('projects', 'doctype', 'project') -execute:frappe.reload_doc('projects', 'doctype', 'project_user') -erpnext.patches.v7_0.convert_timelogbatch_to_timesheet -erpnext.patches.v7_0.convert_timelog_to_timesheet -erpnext.patches.v7_0.move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet -erpnext.patches.v7_0.remove_doctypes_and_reports #2016-10-29 -erpnext.patches.v7_0.update_maintenance_module_in_doctype -erpnext.patches.v7_0.update_prevdoc_values_for_supplier_quotation_item -erpnext.patches.v7_0.rename_advance_table_fields -erpnext.patches.v7_0.rename_salary_components -erpnext.patches.v7_0.rename_prevdoc_fields -erpnext.patches.v7_0.rename_time_sheet_doctype -execute:frappe.delete_doc_if_exists("Report", "Customers Not Buying Since Long Time") -erpnext.patches.v7_0.make_is_group_fieldtype_as_check -execute:frappe.reload_doc('projects', 'doctype', 'timesheet') #2016-09-12 -erpnext.patches.v7_1.rename_field_timesheet -execute:frappe.delete_doc_if_exists("Report", "Employee Holiday Attendance") -execute:frappe.delete_doc_if_exists("DocType", "Payment Tool") -execute:frappe.delete_doc_if_exists("DocType", "Payment Tool Detail") -erpnext.patches.v7_0.setup_account_table_for_expense_claim_type_if_exists -erpnext.patches.v7_0.migrate_schools_to_erpnext -erpnext.patches.v7_1.update_lead_source -erpnext.patches.v6_20x.remove_customer_supplier_roles -erpnext.patches.v7_0.remove_administrator_role_in_doctypes -erpnext.patches.v7_0.rename_fee_amount_to_fee_component -erpnext.patches.v7_0.calculate_total_costing_amount -erpnext.patches.v7_0.fix_nonwarehouse_ledger_gl_entries_for_transactions -erpnext.patches.v7_0.remove_old_earning_deduction_doctypes -erpnext.patches.v7_0.make_guardian -erpnext.patches.v7_0.update_refdoc_in_landed_cost_voucher -erpnext.patches.v7_0.set_material_request_type_in_item -erpnext.patches.v7_0.rename_examination_to_assessment -erpnext.patches.v7_0.set_portal_settings -erpnext.patches.v7_0.update_change_amount_account -erpnext.patches.v7_0.fix_duplicate_icons -erpnext.patches.v7_0.repost_gle_for_pos_sales_return -erpnext.patches.v7_1.update_total_billing_hours -erpnext.patches.v7_1.update_component_type -erpnext.patches.v7_0.repost_gle_for_pos_sales_return -erpnext.patches.v7_0.update_missing_employee_in_timesheet -erpnext.patches.v7_0.update_status_for_timesheet -erpnext.patches.v7_0.set_party_name_in_payment_entry -erpnext.patches.v7_1.set_student_guardian -erpnext.patches.v7_0.update_conversion_factor_in_supplier_quotation_item -erpnext.patches.v7_1.move_sales_invoice_from_parent_to_child_timesheet -execute:frappe.db.sql("update `tabTimesheet` ts, `tabEmployee` emp set ts.employee_name = emp.employee_name where emp.name = ts.employee and ts.employee_name is null and ts.employee is not null") -erpnext.patches.v7_1.fix_link_for_customer_from_lead -execute:frappe.db.sql("delete from `tabTimesheet Detail` where NOT EXISTS (select name from `tabTimesheet` where name = `tabTimesheet Detail`.parent)") -erpnext.patches.v7_0.update_mode_of_payment_type - -execute:frappe.reload_doctype('Employee') #2016-10-18 -execute:frappe.db.sql("update `tabEmployee` set prefered_contact_email = IFNULL(prefered_contact_email,'') ") execute:frappe.reload_doc("Payroll", "doctype", "salary_slip") -execute:frappe.db.sql("update `tabSalary Slip` set posting_date=creation") -execute:frappe.reload_doc("stock", "doctype", "stock_settings") -erpnext.patches.v8_0.create_domain_docs #16-05-2017 -erpnext.patches.v7_1.update_portal_roles -erpnext.patches.v7_1.set_total_amount_currency_in_je -finally:erpnext.patches.v7_0.update_timesheet_communications -erpnext.patches.v7_0.update_status_of_zero_amount_sales_order -erpnext.patches.v7_1.add_field_for_task_dependent -erpnext.patches.v7_0.repost_bin_qty_and_item_projected_qty -erpnext.patches.v7_1.set_prefered_contact_email -execute:frappe.reload_doc('accounts', 'doctype', 'accounts_settings') -execute:frappe.db.set_value("Accounts Settings", "Accounts Settings", "unlink_payment_on_cancellation_of_invoice", 0) -execute:frappe.db.sql("update `tabStock Entry` set total_amount = 0 where purpose in('Repack', 'Manufacture')") -erpnext.patches.v7_1.save_stock_settings -erpnext.patches.v7_0.repost_gle_for_pi_with_update_stock #2016-11-01 -erpnext.patches.v7_1.add_account_user_role_for_timesheet -erpnext.patches.v7_0.set_base_amount_in_invoice_payment_table -erpnext.patches.v7_1.update_invoice_status -erpnext.patches.v7_0.po_status_issue_for_pr_return -erpnext.patches.v7_1.update_missing_salary_component_type -erpnext.patches.v7_1.rename_quality_inspection_field -erpnext.patches.v7_0.update_autoname_field -erpnext.patches.v7_1.update_bom_base_currency -erpnext.patches.v7_0.update_status_of_po_so -erpnext.patches.v7_1.set_budget_against_as_cost_center -erpnext.patches.v7_1.set_currency_exchange_date -erpnext.patches.v7_1.set_sales_person_status -erpnext.patches.v7_1.repost_stock_for_deleted_bins_for_merging_items -erpnext.patches.v7_2.update_website_for_variant -erpnext.patches.v7_2.update_assessment_modules -erpnext.patches.v7_2.update_doctype_status -erpnext.patches.v7_2.update_salary_slips -erpnext.patches.v7_2.delete_fleet_management_module_def -erpnext.patches.v7_2.contact_address_links -erpnext.patches.v7_2.mark_students_active -erpnext.patches.v7_2.set_null_value_to_fields -erpnext.patches.v7_2.update_guardian_name_in_student_master -erpnext.patches.v7_2.update_abbr_in_salary_slips -erpnext.patches.v7_2.rename_evaluation_criteria -erpnext.patches.v7_2.update_party_type -erpnext.patches.v7_2.setup_auto_close_settings -erpnext.patches.v7_2.empty_supplied_items_for_non_subcontracted -erpnext.patches.v7_2.arrear_leave_encashment_as_salary_component -erpnext.patches.v7_2.rename_att_date_attendance -erpnext.patches.v7_2.update_attendance_docstatus -erpnext.patches.v7_2.make_all_assessment_group -erpnext.patches.v8_0.repost_reserved_qty_for_multiple_sales_uom -erpnext.patches.v8_0.addresses_linked_to_lead -execute:frappe.delete_doc('DocType', 'Purchase Common') -erpnext.patches.v8_0.update_stock_qty_value_in_purchase_invoice -erpnext.patches.v8_0.update_supplier_address_in_stock_entry -erpnext.patches.v8_0.rename_is_sample_item_to_allow_zero_valuation_rate -erpnext.patches.v8_0.set_null_to_serial_nos_for_disabled_sales_invoices -erpnext.patches.v8_0.enable_booking_asset_depreciation_automatically -erpnext.patches.v8_0.set_project_copied_from -erpnext.patches.v8_0.update_status_as_paid_for_completed_expense_claim -erpnext.patches.v7_2.stock_uom_in_selling -erpnext.patches.v8_0.revert_manufacturers_table_from_item -erpnext.patches.v8_0.disable_instructor_role -erpnext.patches.v8_0.merge_student_batch_and_student_group -erpnext.patches.v8_0.rename_total_margin_to_rate_with_margin # 11-05-2017 -erpnext.patches.v8_0.fix_status_for_invoices_with_negative_outstanding -erpnext.patches.v8_0.make_payments_table_blank_for_non_pos_invoice -erpnext.patches.v8_0.set_sales_invoice_serial_number_from_delivery_note -erpnext.patches.v8_0.delete_schools_depricated_doctypes -erpnext.patches.v8_0.update_customer_pos_id -erpnext.patches.v8_0.rename_items_in_status_field_of_material_request -erpnext.patches.v8_0.delete_bin_indexes -erpnext.patches.v8_0.move_account_head_from_account_to_warehouse_for_inventory -erpnext.patches.v8_0.change_in_words_varchar_length -erpnext.patches.v8_0.update_stock_qty_value_in_bom_item -erpnext.patches.v8_0.update_sales_cost_in_project -erpnext.patches.v8_0.save_system_settings -erpnext.patches.v8_1.delete_deprecated_reports -erpnext.patches.v9_0.remove_subscription_module -erpnext.patches.v8_7.make_subscription_from_recurring_data erpnext.patches.v8_1.setup_gst_india #2017-06-27 -execute:frappe.reload_doc('regional', 'doctype', 'gst_hsn_code') erpnext.patches.v8_1.removed_roles_from_gst_report_non_indian_account #16-08-2018 -erpnext.patches.v8_1.gst_fixes #2017-07-06 -erpnext.patches.v8_0.update_production_orders -erpnext.patches.v8_1.remove_sales_invoice_from_returned_serial_no -erpnext.patches.v8_1.allow_invoice_copy_to_edit_after_submit -erpnext.patches.v8_1.add_hsn_sac_codes -erpnext.patches.v8_1.update_gst_state #17-07-2017 -erpnext.patches.v8_1.removed_report_support_hours -erpnext.patches.v8_1.add_indexes_in_transaction_doctypes -erpnext.patches.v8_3.set_restrict_to_domain_for_module_def -erpnext.patches.v8_1.update_expense_claim_status -erpnext.patches.v8_3.update_company_total_sales #2017-08-16 -erpnext.patches.v8_4.make_scorecard_records -erpnext.patches.v8_1.set_delivery_date_in_so_item #2017-07-28 -erpnext.patches.v8_5.fix_tax_breakup_for_non_invoice_docs -erpnext.patches.v8_5.remove_quotations_route_in_sidebar -erpnext.patches.v8_5.update_existing_data_in_project_type -erpnext.patches.v8_5.set_default_mode_of_payment -erpnext.patches.v8_5.update_customer_group_in_POS_profile -erpnext.patches.v8_6.update_timesheet_company_from_PO -erpnext.patches.v8_6.set_write_permission_for_quotation_for_sales_manager -erpnext.patches.v8_5.remove_project_type_property_setter erpnext.patches.v8_7.sync_india_custom_fields -erpnext.patches.v8_7.fix_purchase_receipt_status -erpnext.patches.v8_6.rename_bom_update_tool -erpnext.patches.v8_9.add_setup_progress_actions #08-09-2017 #26-09-2017 #22-11-2017 #15-12-2017 -erpnext.patches.v8_9.rename_company_sales_target_field -erpnext.patches.v8_8.set_bom_rate_as_per_uom -erpnext.patches.v8_8.add_new_fields_in_accounts_settings -erpnext.patches.v8_9.set_default_customer_group -erpnext.patches.v8_9.delete_gst_doctypes_for_outside_india_accounts -erpnext.patches.v8_9.set_default_fields_in_variant_settings -erpnext.patches.v8_9.update_billing_gstin_for_indian_account -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.add_healthcare_domain -erpnext.patches.v9_0.set_variant_item_description -erpnext.patches.v9_0.set_uoms_in_variant_field -erpnext.patches.v9_0.copy_old_fees_field_data -execute:frappe.delete_doc_if_exists("DocType", "Program Fee") -erpnext.patches.v9_0.set_pos_profile_name -erpnext.patches.v9_0.remove_non_existing_warehouse_from_stock_settings -execute:frappe.delete_doc_if_exists("DocType", "Program Fee") -erpnext.patches.v8_10.change_default_customer_credit_days -erpnext.patches.v9_0.update_employee_loan_details -erpnext.patches.v9_2.delete_healthcare_domain_default_items -erpnext.patches.v9_1.create_issue_opportunity_type -erpnext.patches.v9_2.rename_translated_domains_in_en -erpnext.patches.v9_0.set_shipping_type_for_existing_shipping_rules -erpnext.patches.v9_0.update_multi_uom_fields_in_material_request -erpnext.patches.v9_2.repost_reserved_qty_for_production -erpnext.patches.v9_2.remove_company_from_patient -erpnext.patches.v9_2.set_item_name_in_production_order -erpnext.patches.v10_0.update_lft_rgt_for_employee -erpnext.patches.v9_2.rename_net_weight_in_item_master -erpnext.patches.v9_2.delete_process_payroll -erpnext.patches.v10_0.add_agriculture_domain -erpnext.patches.v10_0.add_non_profit_domain -erpnext.patches.v10_0.setup_vat_for_uae_and_saudi_arabia #2017-12-28 -erpnext.patches.v10_0.set_primary_contact_for_customer -erpnext.patches.v10_0.copy_projects_renamed_fields -erpnext.patches.v10_0.enabled_regional_print_format_based_on_country -erpnext.patches.v10_0.update_asset_calculate_depreciation -erpnext.patches.v10_0.add_guardian_role_for_parent_portal -erpnext.patches.v10_0.set_numeric_ranges_in_template_if_blank -erpnext.patches.v10_0.update_reserved_qty_for_purchase_order erpnext.patches.v10_0.fichier_des_ecritures_comptables_for_france -erpnext.patches.v10_0.update_assessment_plan -erpnext.patches.v10_0.update_assessment_result -erpnext.patches.v10_0.set_default_payment_terms_based_on_company -erpnext.patches.v10_0.update_sales_order_link_to_purchase_order erpnext.patches.v10_0.rename_price_to_rate_in_pricing_rule erpnext.patches.v10_0.set_currency_in_pricing_rule -erpnext.patches.v10_0.set_b2c_limit erpnext.patches.v10_0.update_translatable_fields erpnext.patches.v10_0.rename_offer_letter_to_job_offer execute:frappe.delete_doc('DocType', 'Production Planning Tool', ignore_missing=True) @@ -496,16 +21,6 @@ erpnext.patches.v10_0.migrate_daily_work_summary_settings_to_daily_work_summary_ erpnext.patches.v10_0.add_default_cash_flow_mappers erpnext.patches.v11_0.rename_duplicate_item_code_values erpnext.patches.v11_0.make_quality_inspection_template -erpnext.patches.v10_0.update_status_for_multiple_source_in_po -erpnext.patches.v10_0.set_auto_created_serial_no_in_stock_entry -erpnext.patches.v10_0.update_territory_and_customer_group -erpnext.patches.v10_0.update_warehouse_address_details -erpnext.patches.v10_0.update_reserved_qty_for_purchase_order -erpnext.patches.v10_0.update_hub_connector_domain -erpnext.patches.v10_0.set_student_party_type -erpnext.patches.v10_0.update_project_in_sle -erpnext.patches.v10_0.fix_reserved_qty_for_sub_contract -erpnext.patches.v10_0.repost_requested_qty_for_non_stock_uom_items erpnext.patches.v11_0.merge_land_unit_with_location erpnext.patches.v11_0.add_index_on_nestedset_doctypes erpnext.patches.v11_0.remove_modules_setup_page @@ -514,7 +29,6 @@ erpnext.patches.v11_0.update_department_lft_rgt erpnext.patches.v11_0.add_default_email_template_for_leave erpnext.patches.v11_0.set_default_email_template_in_hr #08-06-2018 erpnext.patches.v11_0.uom_conversion_data #30-06-2018 -erpnext.patches.v10_0.taxes_issue_with_pos erpnext.patches.v11_0.update_account_type_in_party_type erpnext.patches.v11_0.rename_healthcare_doctype_and_fields erpnext.patches.v11_0.rename_supplier_type_to_supplier_group @@ -522,8 +36,6 @@ erpnext.patches.v10_1.transfer_subscription_to_auto_repeat erpnext.patches.v11_0.update_brand_in_item_price erpnext.patches.v11_0.create_default_success_action erpnext.patches.v11_0.add_healthcare_service_unit_tree_root -erpnext.patches.v10_0.set_qty_in_transactions_based_on_serial_no_input -erpnext.patches.v10_0.show_leaves_of_all_department_members_in_calendar erpnext.patches.v11_0.rename_field_max_days_allowed erpnext.patches.v11_0.create_salary_structure_assignments erpnext.patches.v11_0.rename_health_insurance @@ -536,7 +48,6 @@ erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany #02-07- erpnext.patches.v11_0.refactor_erpnext_shopify #2018-09-07 erpnext.patches.v11_0.rename_overproduction_percent_field erpnext.patches.v11_0.update_backflush_subcontract_rm_based_on_bom -erpnext.patches.v10_0.update_status_in_purchase_receipt erpnext.patches.v11_0.inter_state_field_for_gst erpnext.patches.v11_0.rename_members_with_naming_series #04-06-2018 erpnext.patches.v11_0.set_update_field_and_value_in_workflow_state @@ -550,13 +61,10 @@ erpnext.patches.v11_0.skip_user_permission_check_for_department erpnext.patches.v11_0.set_department_for_doctypes erpnext.patches.v11_0.update_allow_transfer_for_manufacture erpnext.patches.v11_0.add_item_group_defaults -erpnext.patches.v10_0.update_address_template_for_india erpnext.patches.v11_0.add_expense_claim_default_account execute:frappe.delete_doc("Page", "hub") erpnext.patches.v11_0.reset_publish_in_hub_for_all_items erpnext.patches.v11_0.update_hub_url # 2018-08-31 # 2018-09-03 -erpnext.patches.v10_0.set_discount_amount -erpnext.patches.v10_0.recalculate_gross_margin_for_project erpnext.patches.v11_0.make_job_card erpnext.patches.v11_0.redesign_healthcare_billing_work_flow erpnext.patches.v10_0.delete_hub_documents # 12-08-2018 @@ -570,9 +78,6 @@ execute:frappe.delete_doc_if_exists("Page", "stock-analytics") execute:frappe.delete_doc_if_exists("Page", "production-analytics") erpnext.patches.v11_0.ewaybill_fields_gst_india #2018-11-13 #2019-01-09 #2019-04-01 #2019-04-26 #2019-05-03 erpnext.patches.v11_0.drop_column_max_days_allowed -erpnext.patches.v10_0.update_user_image_in_employee -erpnext.patches.v10_0.repost_gle_for_purchase_receipts_with_rejected_items -erpnext.patches.v10_0.allow_operators_in_supplier_scorecard erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019 erpnext.patches.v11_0.update_delivery_trip_status erpnext.patches.v11_0.set_missing_gst_hsn_code From 43a24f2aa87eef372cf2fddf1b99ef68245cb643 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 3 Jun 2021 20:05:00 +0530 Subject: [PATCH 121/180] ci: Update ERPNext backup patch test >= v10 * Generated v10 backup archive * used old v7 erpnext backup hosted via build.erpnext.com * upgraded to v10 frappe + erpnext * Hosted backup on https://erpnext.com/files/v10-erpnext.sql.gz --- .github/workflows/patch.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml index 7c9e0272c9..b96a3d6bbe 100644 --- a/.github/workflows/patch.yml +++ b/.github/workflows/patch.yml @@ -66,4 +66,8 @@ jobs: run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh - name: Run Patch Tests - run: cd ~/frappe-bench/ && wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz && bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz && bench --site test_site migrate + run: | + cd ~/frappe-bench/ + wget https://erpnext.com/files/v10-erpnext.sql.gz + bench --site test_site --force restore ~/frappe-bench/v10-erpnext.sql.gz + bench --site test_site migrate From 96f8ebc308ba2ceada92ab5a46ec582a7a9ba15f Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Jun 2021 17:07:45 +0530 Subject: [PATCH 122/180] fix(patch): Handle NULL values from fieldtype change --- erpnext/patches/v11_0/rename_bom_wo_fields.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/patches/v11_0/rename_bom_wo_fields.py b/erpnext/patches/v11_0/rename_bom_wo_fields.py index b4a740fabb..882ec84e64 100644 --- a/erpnext/patches/v11_0/rename_bom_wo_fields.py +++ b/erpnext/patches/v11_0/rename_bom_wo_fields.py @@ -6,6 +6,10 @@ import frappe from frappe.model.utils.rename_field import rename_field def execute(): + # updating column value to handle field change from Data to Currency + changed_field = "base_scrap_material_cost" + frappe.db.sql(f"update `tabBOM` set {changed_field} = '0' where trim(coalesce({changed_field}, ''))= ''") + for doctype in ['BOM Explosion Item', 'BOM Item', 'Work Order Item', 'Item']: if frappe.db.has_column(doctype, 'allow_transfer_for_manufacture'): if doctype != 'Item': From 137d08a9d7bddb5903e5b01612a07c5a1223c378 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Jun 2021 20:11:34 +0530 Subject: [PATCH 123/180] fix: Manually link_fields from flags before rename_doc --- .../v13_0/healthcare_lab_module_rename_doctypes.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py b/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py index 9af0a8dbef..2549a1e91e 100644 --- a/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py +++ b/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import frappe from frappe.model.utils.rename_field import rename_field + def execute(): if frappe.db.exists('DocType', 'Lab Test') and frappe.db.exists('DocType', 'Lab Test Template'): # rename child doctypes @@ -17,7 +18,12 @@ def execute(): frappe.reload_doc('healthcare', 'doctype', 'lab_test_template') for old_dt, new_dt in doctypes.items(): - if not frappe.db.table_exists(new_dt) and frappe.db.table_exists(old_dt): + frappe.flags.link_fields = {} + should_rename = ( + frappe.db.table_exists(old_dt) + and not frappe.db.table_exists(new_dt) + ) + if should_rename: frappe.reload_doc('healthcare', 'doctype', frappe.scrub(old_dt)) frappe.rename_doc('DocType', old_dt, new_dt, force=True) frappe.reload_doc('healthcare', 'doctype', frappe.scrub(new_dt)) From 88176e65e46c43f1a5a6e88f3d08a2bcb6e3e5fb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 10 Jun 2021 17:31:28 +0530 Subject: [PATCH 124/180] fix: Check for gst_hsn_code --- 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 5a53d49399..11ebef724c 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -194,7 +194,7 @@ def get_item_list(invoice): 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 - item.is_service_item = 'Y' if item.gst_hsn_code[:2] == "99" else 'N' + item.is_service_item = 'Y' if item.gst_hsn_code and item.gst_hsn_code[:2] == "99" else 'N' item.serial_no = "" item = update_item_taxes(invoice, item) From a5c3427293a8bfeb38bd83c08fe155b9d33179a4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 10 Jun 2021 18:09:49 +0530 Subject: [PATCH 125/180] fix: Sort account balances by account name --- .../report/account_balance/account_balance.py | 4 +--- .../account_balance/test_account_balance.py | 22 +++++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/report/account_balance/account_balance.py b/erpnext/accounts/report/account_balance/account_balance.py index 65e7d789bb..be64c327fd 100644 --- a/erpnext/accounts/report/account_balance/account_balance.py +++ b/erpnext/accounts/report/account_balance/account_balance.py @@ -58,11 +58,9 @@ def get_conditions(filters): def get_data(filters): data = [] - conditions = get_conditions(filters) - accounts = frappe.db.get_all("Account", fields=["name", "account_currency"], - filters=conditions) + filters=conditions, order_by='name') for d in accounts: balance = get_balance_on(d.name, date=filters.report_date) diff --git a/erpnext/accounts/report/account_balance/test_account_balance.py b/erpnext/accounts/report/account_balance/test_account_balance.py index b6ced312d0..14ddf4a30f 100644 --- a/erpnext/accounts/report/account_balance/test_account_balance.py +++ b/erpnext/accounts/report/account_balance/test_account_balance.py @@ -23,7 +23,7 @@ class TestAccountBalance(unittest.TestCase): expected_data = [ { - "account": 'Sales - _TC2', + "account": 'Direct Income - _TC2', "currency": 'EUR', "balance": -100.0, }, @@ -32,21 +32,21 @@ class TestAccountBalance(unittest.TestCase): "currency": 'EUR', "balance": -100.0, }, - { - "account": 'Service - _TC2', - "currency": 'EUR', - "balance": 0.0, - }, - { - "account": 'Direct Income - _TC2', - "currency": 'EUR', - "balance": -100.0, - }, { "account": 'Indirect Income - _TC2', "currency": 'EUR', "balance": 0.0, }, + { + "account": 'Sales - _TC2', + "currency": 'EUR', + "balance": -100.0, + }, + { + "account": 'Service - _TC2', + "currency": 'EUR', + "balance": 0.0, + } ] self.assertEqual(expected_data, report[1]) From 9a243ed4699f2ae9e96397b221193f1fe40a2cb0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 10 Jun 2021 18:09:49 +0530 Subject: [PATCH 126/180] fix: Sort account balances by account name --- .../report/account_balance/account_balance.py | 4 +--- .../account_balance/test_account_balance.py | 22 +++++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/report/account_balance/account_balance.py b/erpnext/accounts/report/account_balance/account_balance.py index 65e7d789bb..be64c327fd 100644 --- a/erpnext/accounts/report/account_balance/account_balance.py +++ b/erpnext/accounts/report/account_balance/account_balance.py @@ -58,11 +58,9 @@ def get_conditions(filters): def get_data(filters): data = [] - conditions = get_conditions(filters) - accounts = frappe.db.get_all("Account", fields=["name", "account_currency"], - filters=conditions) + filters=conditions, order_by='name') for d in accounts: balance = get_balance_on(d.name, date=filters.report_date) diff --git a/erpnext/accounts/report/account_balance/test_account_balance.py b/erpnext/accounts/report/account_balance/test_account_balance.py index b6ced312d0..14ddf4a30f 100644 --- a/erpnext/accounts/report/account_balance/test_account_balance.py +++ b/erpnext/accounts/report/account_balance/test_account_balance.py @@ -23,7 +23,7 @@ class TestAccountBalance(unittest.TestCase): expected_data = [ { - "account": 'Sales - _TC2', + "account": 'Direct Income - _TC2', "currency": 'EUR', "balance": -100.0, }, @@ -32,21 +32,21 @@ class TestAccountBalance(unittest.TestCase): "currency": 'EUR', "balance": -100.0, }, - { - "account": 'Service - _TC2', - "currency": 'EUR', - "balance": 0.0, - }, - { - "account": 'Direct Income - _TC2', - "currency": 'EUR', - "balance": -100.0, - }, { "account": 'Indirect Income - _TC2', "currency": 'EUR', "balance": 0.0, }, + { + "account": 'Sales - _TC2', + "currency": 'EUR', + "balance": -100.0, + }, + { + "account": 'Service - _TC2', + "currency": 'EUR', + "balance": 0.0, + } ] self.assertEqual(expected_data, report[1]) From 9a67141bfb0069d9254a902dd2f366d7a22b36be Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 10 Jun 2021 18:48:55 +0530 Subject: [PATCH 127/180] fix(einvoicing): service item check (#26010) --- 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 a1179ff9b6..0eaf790538 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -199,7 +199,7 @@ def get_item_list(invoice): 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 - item.is_service_item = 'Y' if item.gst_hsn_code[:2] == "99" else 'N' + item.is_service_item = 'Y' if item.gst_hsn_code and item.gst_hsn_code[:2] == "99" else 'N' item.serial_no = "" item = update_item_taxes(invoice, item) From 687ad9b9421074a073d3087e3c266a72473bb7c7 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 10 Jun 2021 21:18:19 +0530 Subject: [PATCH 128/180] fix: Report Raw Materials to be Transferred --- ...tracted_raw_materials_to_be_transferred.py | 118 ++++++------------ 1 file changed, 39 insertions(+), 79 deletions(-) diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py index de2ae8fc73..5a0381b017 100644 --- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py +++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py @@ -9,10 +9,10 @@ def execute(filters=None): if filters.from_date >= filters.to_date: frappe.msgprint(_("To Date must be greater than From Date")) - data = [] columns = get_columns() - get_data(data , filters) - return columns, data + data = get_data(filters) + + return columns, data or [] def get_columns(): return [ @@ -21,13 +21,12 @@ def get_columns(): "fieldtype": "Link", "fieldname": "purchase_order", "options": "Purchase Order", - "width": 150 + "width": 200 }, { "label": _("Date"), "fieldtype": "Date", "fieldname": "date", - "hidden": 1, "width": 150 }, { @@ -41,97 +40,58 @@ def get_columns(): "label": _("Item Code"), "fieldtype": "Data", "fieldname": "rm_item_code", - "width": 100 + "width": 150 }, { "label": _("Required Quantity"), "fieldtype": "Float", - "fieldname": "r_qty", - "width": 100 + "fieldname": "reqd_qty", + "width": 150 }, { "label": _("Transferred Quantity"), "fieldtype": "Float", - "fieldname": "t_qty", - "width": 100 + "fieldname": "transferred_qty", + "width": 200 }, { "label": _("Pending Quantity"), "fieldtype": "Float", "fieldname": "p_qty", - "width": 100 + "width": 150 } ] -def get_data(data, filters): - po = get_po(filters) - po_transferred_qty_map = frappe._dict(get_transferred_quantity([v.name for v in po])) +def get_data(filters): + po_rm_item_details = get_po_items_to_supply(filters) - sub_items = get_purchase_order_item_supplied([v.name for v in po]) + data = [] + for row in po_rm_item_details: + transferred_qty = row.get("transferred_qty") or 0 + if transferred_qty < row.get("reqd_qty", 0): + pending_qty = frappe.utils.flt(row.get("reqd_qty", 0) - transferred_qty) + row.p_qty = pending_qty if pending_qty > 0 else 0 + data.append(row) - for order in po: - for item in sub_items: - if order.name == item.parent and order.name in po_transferred_qty_map and \ - item.required_qty != po_transferred_qty_map.get(order.name).get(item.rm_item_code): - transferred_qty = po_transferred_qty_map.get(order.name).get(item.rm_item_code) \ - if po_transferred_qty_map.get(order.name).get(item.rm_item_code) else 0 - row ={ - 'purchase_order': item.parent, - 'date': order.transaction_date, - 'supplier': order.supplier, - 'rm_item_code': item.rm_item_code, - 'r_qty': item.required_qty, - 't_qty':transferred_qty, - 'p_qty':item.required_qty - transferred_qty - } + return data - data.append(row) - - return(data) - -def get_po(filters): - record_filters = [ - ["is_subcontracted", "=", "Yes"], - ["supplier", "=", filters.supplier], - ["transaction_date", "<=", filters.to_date], - ["transaction_date", ">=", filters.from_date], - ["docstatus", "=", 1] - ] - return frappe.get_all("Purchase Order", filters=record_filters, fields=["name", "transaction_date", "supplier"]) - -def get_transferred_quantity(po_name): - stock_entries = get_stock_entry(po_name) - stock_entries_detail = get_stock_entry_detail([v.name for v in stock_entries]) - po_transferred_qty_map = {} - - - for entry in stock_entries: - for details in stock_entries_detail: - if details.parent == entry.name: - details["Purchase_order"] = entry.purchase_order - if entry.purchase_order not in po_transferred_qty_map: - po_transferred_qty_map[entry.purchase_order] = {} - po_transferred_qty_map[entry.purchase_order][details.item_code] = details.qty - else: - po_transferred_qty_map[entry.purchase_order][details.item_code] = po_transferred_qty_map[entry.purchase_order].get(details.item_code, 0) + details.qty - - return po_transferred_qty_map - - -def get_stock_entry(po): - return frappe.get_all("Stock Entry", filters=[ - ('purchase_order', 'IN', po), - ('stock_entry_type', '=', 'Send to Subcontractor'), - ('docstatus', '=', 1) - ], fields=["name", "purchase_order"]) - -def get_stock_entry_detail(se): - return frappe.get_all("Stock Entry Detail", filters=[ - ["parent", "in", se] +def get_po_items_to_supply(filters): + return frappe.db.get_all( + "Purchase Order", + fields=[ + "name as purchase_order", + "transaction_date as date", + "supplier as supplier", + "`tabPurchase Order Item Supplied`.rm_item_code as rm_item_code", + "`tabPurchase Order Item Supplied`.required_qty as reqd_qty", + "`tabPurchase Order Item Supplied`.supplied_qty as transferred_qty" ], - fields=["parent", "item_code", "qty"]) - -def get_purchase_order_item_supplied(po): - return frappe.get_all("Purchase Order Item Supplied", filters=[ - ('parent', 'IN', po) - ], fields=['parent', 'rm_item_code', 'required_qty']) + filters = [ + ["Purchase Order", "per_received", "<", "100"], + ["Purchase Order", "is_subcontracted", "=", "Yes"], + ["Purchase Order", "supplier", "=", filters.supplier], + ["Purchase Order", "transaction_date", "<=", filters.to_date], + ["Purchase Order", "transaction_date", ">=", filters.from_date], + ["Purchase Order", "docstatus", "=", 1] + ] + ) \ No newline at end of file From ec4a3723cc1343e1ce99d9114ecb9bd610f8c034 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 11 Jun 2021 12:47:06 +0530 Subject: [PATCH 129/180] fix: Sider --- .../subcontracted_raw_materials_to_be_transferred.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py index 5a0381b017..68426abbb0 100644 --- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py +++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py @@ -70,7 +70,7 @@ def get_data(filters): transferred_qty = row.get("transferred_qty") or 0 if transferred_qty < row.get("reqd_qty", 0): pending_qty = frappe.utils.flt(row.get("reqd_qty", 0) - transferred_qty) - row.p_qty = pending_qty if pending_qty > 0 else 0 + row.p_qty = pending_qty if pending_qty > 0 else 0 data.append(row) return data From 5bb89b0ead96f0bb7e2346fb01bd8710bbb7a3b2 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 11 Jun 2021 15:57:01 +0530 Subject: [PATCH 130/180] test(perf): eliminate repeat creation of HSN codes (#25947) --- erpnext/regional/india/setup.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 229e0c031e..3e0b9b733b 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -27,6 +27,9 @@ def setup_company_independent_fixtures(patch=False): add_print_formats() def add_hsn_sac_codes(): + if frappe.flags.in_test and frappe.flags.created_hsn_codes: + return + # HSN codes with open(os.path.join(os.path.dirname(__file__), 'hsn_code_data.json'), 'r') as f: hsn_codes = json.loads(f.read()) @@ -38,6 +41,9 @@ def add_hsn_sac_codes(): sac_codes = json.loads(f.read()) create_hsn_codes(sac_codes, code_field="sac_code") + if frappe.flags.in_test: + frappe.flags.created_hsn_codes = True + def create_hsn_codes(data, code_field): for d in data: hsn_code = frappe.new_doc('GST HSN Code') From a9c84f749a64f2627885d61d571bf10c04fd61a8 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 11 Jun 2021 16:00:48 +0530 Subject: [PATCH 131/180] perf(minor): remove unnecessary comprehensions (#25645) --- erpnext/accounts/doctype/c_form/c_form.py | 2 +- .../doctype/coupon_code/coupon_code.py | 2 +- .../invoice_discounting/invoice_discounting.py | 2 +- .../doctype/journal_entry/journal_entry.py | 4 ++-- .../monthly_distribution.py | 2 +- .../doctype/payment_entry/payment_entry.py | 10 +++++----- .../doctype/payment_request/payment_request.py | 2 +- erpnext/accounts/doctype/pricing_rule/utils.py | 18 +++++++++--------- .../purchase_invoice/test_purchase_invoice.py | 2 +- .../test_tax_withholding_category.py | 2 +- erpnext/accounts/general_ledger.py | 2 +- .../report/cash_flow/custom_cash_flow.py | 8 ++++---- .../customer_ledger_summary.py | 2 +- .../accounts/report/financial_statements.py | 2 +- .../item_wise_purchase_register.py | 2 +- .../item_wise_sales_register.py | 2 +- .../report/pos_register/pos_register.py | 6 +++--- .../purchase_register/purchase_register.py | 14 +++++++------- .../sales_payment_summary.py | 4 ++-- .../report/sales_register/sales_register.py | 16 ++++++++-------- .../tds_computation_summary.py | 4 ++-- erpnext/accounts/report/utils.py | 2 +- erpnext/accounts/utils.py | 2 +- erpnext/assets/doctype/asset/test_asset.py | 2 +- .../asset_value_adjustment.py | 2 +- .../doctype/purchase_order/purchase_order.py | 6 +++--- .../purchase_order/test_purchase_order.py | 2 +- .../request_for_quotation.py | 2 +- erpnext/controllers/accounts_controller.py | 8 ++++---- erpnext/controllers/buying_controller.py | 6 +++--- erpnext/controllers/queries.py | 2 +- erpnext/controllers/selling_controller.py | 2 +- erpnext/controllers/status_updater.py | 4 ++-- erpnext/controllers/stock_controller.py | 6 +++--- erpnext/controllers/taxes_and_totals.py | 12 ++++++------ erpnext/controllers/tests/test_mapper.py | 4 ++-- .../controllers/website_list_for_contact.py | 2 +- .../manufacturing/doctype/job_card/job_card.py | 2 +- erpnext/projects/doctype/task/task.py | 2 +- .../projects/doctype/timesheet/timesheet.py | 4 ++-- .../report/project_summary/project_summary.py | 2 +- erpnext/selling/doctype/customer/customer.py | 2 +- erpnext/selling/doctype/quotation/quotation.py | 2 +- .../selling/doctype/sales_order/sales_order.py | 2 +- erpnext/setup/doctype/company/company.py | 4 ++-- erpnext/setup/doctype/item_group/item_group.py | 4 ++-- erpnext/stock/doctype/batch/batch.py | 2 +- .../doctype/delivery_note/delivery_note.py | 2 +- .../doctype/delivery_trip/delivery_trip.py | 6 +++--- .../landed_cost_voucher/landed_cost_voucher.py | 8 ++++---- .../test_landed_cost_voucher.py | 2 +- .../stock/doctype/packing_slip/packing_slip.py | 4 ++-- .../purchase_receipt/test_purchase_receipt.py | 2 +- erpnext/stock/doctype/serial_no/serial_no.py | 2 +- .../stock/doctype/stock_entry/stock_entry.py | 2 +- .../item_price_stock/item_price_stock.py | 2 +- .../itemwise_recommended_reorder_level.py | 2 +- .../product_bundle_balance.py | 2 +- .../report/stock_balance/stock_balance.py | 6 +++--- .../stock/report/stock_ledger/stock_ledger.py | 4 ++-- erpnext/stock/stock_ledger.py | 2 +- .../doctype/warranty_claim/warranty_claim.py | 2 +- erpnext/utilities/product.py | 2 +- erpnext/utilities/transaction_base.py | 4 ++-- 64 files changed, 126 insertions(+), 126 deletions(-) diff --git a/erpnext/accounts/doctype/c_form/c_form.py b/erpnext/accounts/doctype/c_form/c_form.py index fd86ed4c90..cfe28f3ff9 100644 --- a/erpnext/accounts/doctype/c_form/c_form.py +++ b/erpnext/accounts/doctype/c_form/c_form.py @@ -54,7 +54,7 @@ class CForm(Document): frappe.throw(_("Please enter atleast 1 invoice in the table")) def set_total_invoiced_amount(self): - total = sum([flt(d.grand_total) for d in self.get('invoices')]) + total = sum(flt(d.grand_total) for d in self.get('invoices')) frappe.db.set(self, 'total_invoiced_amount', total) @frappe.whitelist() diff --git a/erpnext/accounts/doctype/coupon_code/coupon_code.py b/erpnext/accounts/doctype/coupon_code/coupon_code.py index 7829c9320d..55c119315e 100644 --- a/erpnext/accounts/doctype/coupon_code/coupon_code.py +++ b/erpnext/accounts/doctype/coupon_code/coupon_code.py @@ -14,7 +14,7 @@ class CouponCode(Document): if not self.coupon_code: if self.coupon_type == "Promotional": - self.coupon_code =''.join([i for i in self.coupon_name if not i.isdigit()])[0:8].upper() + self.coupon_code =''.join(i for i in self.coupon_name if not i.isdigit())[0:8].upper() elif self.coupon_type == "Gift Card": self.coupon_code = frappe.generate_hash()[:10].upper() diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py index 95d2ee56d9..b73d8bfbb1 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py @@ -42,7 +42,7 @@ class InvoiceDiscounting(AccountsController): record.idx, frappe.bold(actual_outstanding), frappe.bold(record.sales_invoice))) def calculate_total_amount(self): - self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices]) + self.total_amount = sum(flt(d.outstanding_amount) for d in self.invoices) def on_submit(self): self.update_sales_invoice() diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index ed1bd28223..937597bc55 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -196,8 +196,8 @@ class JournalEntry(AccountsController): frappe.throw(_("Row {0}: Party Type and Party is required for Receivable / Payable account {1}").format(d.idx, d.account)) def check_credit_limit(self): - customers = list(set([d.party for d in self.get("accounts") - if d.party_type=="Customer" and d.party and flt(d.debit) > 0])) + customers = list(set(d.party for d in self.get("accounts") + if d.party_type=="Customer" and d.party and flt(d.debit) > 0)) if customers: from erpnext.selling.doctype.customer.customer import check_credit_limit for customer in customers: diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py index 88667d7207..bff6422732 100644 --- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py +++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py @@ -21,7 +21,7 @@ class MonthlyDistribution(Document): idx += 1 def validate(self): - total = sum([flt(d.percentage_allocation) for d in self.get("percentages")]) + total = sum(flt(d.percentage_allocation) for d in self.get("percentages")) if flt(total, 2) != 100.0: frappe.throw(_("Percentage Allocation should be equal to 100%") + \ diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index edca210142..2c6deb3896 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -309,7 +309,7 @@ class PaymentEntry(AccountsController): for k, v in no_oustanding_refs.items(): frappe.msgprint( _("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.") - .format(k, frappe.bold(", ".join([d.reference_name for d in v])), frappe.bold("negative outstanding amount")) + .format(k, frappe.bold(", ".join(d.reference_name for d in v)), frappe.bold("negative outstanding amount")) + "

" + _("If this is undesirable please cancel the corresponding Payment Entry."), title=_("Warning"), indicator="orange") @@ -524,7 +524,7 @@ class PaymentEntry(AccountsController): def set_unallocated_amount(self): self.unallocated_amount = 0 if self.party: - total_deductions = sum([flt(d.amount) for d in self.get("deductions")]) + total_deductions = sum(flt(d.amount) for d in self.get("deductions")) if self.payment_type == "Receive" \ and self.base_total_allocated_amount < self.base_received_amount_after_tax + total_deductions \ and self.total_allocated_amount < self.paid_amount_after_tax + (total_deductions / self.source_exchange_rate): @@ -549,7 +549,7 @@ class PaymentEntry(AccountsController): else: self.difference_amount = self.base_paid_amount_after_tax - flt(self.base_received_amount_after_tax) - total_deductions = sum([flt(d.amount) for d in self.get("deductions")]) + total_deductions = sum(flt(d.amount) for d in self.get("deductions")) self.difference_amount = flt(self.difference_amount - total_deductions, self.precision("difference_amount")) @@ -565,8 +565,8 @@ class PaymentEntry(AccountsController): if ((self.payment_type=="Pay" and self.party_type=="Customer") or (self.payment_type=="Receive" and self.party_type=="Supplier")): - total_negative_outstanding = sum([abs(flt(d.outstanding_amount)) - for d in self.get("references") if flt(d.outstanding_amount) < 0]) + total_negative_outstanding = sum(abs(flt(d.outstanding_amount)) + for d in self.get("references") if flt(d.outstanding_amount) < 0) paid_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount additional_charges = sum([flt(d.amount) for d in self.deductions]) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 468978785b..438951db62 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -112,7 +112,7 @@ class PaymentRequest(Document): 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]) + request_amounts = sum(json.loads(d).get('request_amount') for d in data_of_completed_requests) return request_amounts def on_cancel(self): diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index d23b952bdc..b54d0e73a8 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -20,9 +20,9 @@ from frappe.utils import cint, flt, get_link_to_form, getdate, today, fmt_money class MultiplePricingRuleConflict(frappe.ValidationError): pass apply_on_table = { - 'Item Code': 'items', - 'Item Group': 'item_groups', - 'Brand': 'brands' + 'Item Code': 'items', + 'Item Group': 'item_groups', + 'Brand': 'brands' } def get_pricing_rules(args, doc=None): @@ -183,7 +183,7 @@ def _get_tree_conditions(args, parenttype, table, allow_blank=True): condition = "ifnull({table}.{field}, '') in ({parent_groups})".format( table=table, field=field, - parent_groups=", ".join([frappe.db.escape(d) for d in parent_groups]) + parent_groups=", ".join(frappe.db.escape(d) for d in parent_groups) ) frappe.flags.tree_conditions[key] = condition @@ -264,7 +264,7 @@ def filter_pricing_rules(args, pricing_rules, doc=None): # find pricing rule with highest priority if pricing_rules: - max_priority = max([cint(p.priority) for p in pricing_rules]) + max_priority = max(cint(p.priority) for p in pricing_rules) if max_priority: pricing_rules = list(filter(lambda x: cint(x.priority)==max_priority, pricing_rules)) @@ -272,14 +272,14 @@ def filter_pricing_rules(args, pricing_rules, doc=None): pricing_rules = list(pricing_rules) if len(pricing_rules) > 1: - rate_or_discount = list(set([d.rate_or_discount for d in pricing_rules])) + rate_or_discount = list(set(d.rate_or_discount for d in pricing_rules)) if len(rate_or_discount) == 1 and rate_or_discount[0] == "Discount Percentage": pricing_rules = list(filter(lambda x: x.for_price_list==args.price_list, pricing_rules)) \ or pricing_rules if len(pricing_rules) > 1 and not args.for_shopping_cart: frappe.throw(_("Multiple Price Rules exists with same criteria, please resolve conflict by assigning priority. Price Rules: {0}") - .format("\n".join([d.name for d in pricing_rules])), MultiplePricingRuleConflict) + .format("\n".join(d.name for d in pricing_rules)), MultiplePricingRuleConflict) elif pricing_rules: return pricing_rules[0] @@ -541,7 +541,7 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False): if pricing_rule_args: - items = tuple([(d.item_code, d.pricing_rules) for d in doc.items if d.is_free_item]) + items = tuple((d.item_code, d.pricing_rules) for d in doc.items if d.is_free_item) for args in pricing_rule_args: if not items or (args.get('item_code'), args.get('pricing_rules')) not in items: @@ -589,4 +589,4 @@ def update_coupon_code_count(coupon_name,transaction_type): elif transaction_type=='cancelled': if coupon.used>0: coupon.used=coupon.used-1 - coupon.save(ignore_permissions=True) \ No newline at end of file + coupon.save(ignore_permissions=True) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 723d151ad8..503dda7728 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -632,7 +632,7 @@ 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")]) + rm_supp_cost = sum(d.amount for d in pi.get("supplied_items")) self.assertEqual(flt(pi.get("items")[0].rm_supp_cost, 2), flt(rm_supp_cost, 2)) def test_rejected_serial_no(self): 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 0cea7612dd..dd26be7c99 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 @@ -112,7 +112,7 @@ class TestTaxWithholdingCategory(unittest.TestCase): 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']) + 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) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index d4b249429b..59009ae621 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -143,7 +143,7 @@ def make_entry(args, adv_adj, update_outstanding, from_repost=False): validate_expense_against_budget(args) def validate_cwip_accounts(gl_map): - cwip_enabled = any([cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")]) + cwip_enabled = any(cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")) if cwip_enabled and gl_map[0].voucher_type == "Journal Entry": cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount diff --git a/erpnext/accounts/report/cash_flow/custom_cash_flow.py b/erpnext/accounts/report/cash_flow/custom_cash_flow.py index ff87276a87..c11c15390b 100644 --- a/erpnext/accounts/report/cash_flow/custom_cash_flow.py +++ b/erpnext/accounts/report/cash_flow/custom_cash_flow.py @@ -32,7 +32,7 @@ def get_accounts_in_mappers(mapping_names): join `tabCash Flow Mapping` cfm on cfma.parent=cfm.name where cfma.parent in (%s) order by cfm.is_working_capital - ''', (', '.join(['"%s"' % d for d in mapping_names]))) + ''', (', '.join('"%s"' % d for d in mapping_names))) def setup_mappers(mappers): @@ -83,8 +83,8 @@ def setup_mappers(mappers): account_types_labels = sorted( set( - [(d['label'], d['is_working_capital'], d['is_income_tax_liability'], d['is_income_tax_expense']) - for d in account_types] + (d['label'], d['is_working_capital'], d['is_income_tax_liability'], d['is_income_tax_expense']) + for d in account_types ), key=lambda x: x[1] ) @@ -375,7 +375,7 @@ def _get_account_type_based_data(filters, account_names, period_list, accumulate total = 0 for period in period_list: start_date = get_start_date(period, accumulated_values, company) - accounts = ', '.join(['"%s"' % d for d in account_names]) + accounts = ', '.join('"%s"' % d for d in account_names) if opening_balances: date_info = dict(date=start_date) diff --git a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py index 10b32fea56..c79d7401e6 100644 --- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py +++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py @@ -145,7 +145,7 @@ class PartyLedgerSummaryReport(object): out = [] for party, row in iteritems(self.party_data): if row.opening_balance or row.invoiced_amount or row.paid_amount or row.return_amount or row.closing_amount: - total_party_adjustment = sum([amount for amount in itervalues(self.party_adjustment_details.get(party, {}))]) + total_party_adjustment = sum(amount for amount in itervalues(self.party_adjustment_details.get(party, {}))) row.paid_amount -= total_party_adjustment adjustments = self.party_adjustment_details.get(party, {}) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index d20ddbde5c..39ff804518 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -369,7 +369,7 @@ def set_gl_entries_by_account( if accounts: additional_conditions += " and account in ({})"\ - .format(", ".join([frappe.db.escape(d) for d in accounts])) + .format(", ".join(frappe.db.escape(d) for d in accounts)) gl_filters = { "company": company, 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 cb4d9b43db..685419a17e 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 @@ -334,7 +334,7 @@ def get_aii_accounts(): def get_purchase_receipts_against_purchase_order(item_list): po_pr_map = frappe._dict() - po_item_rows = list(set([d.po_detail for d in item_list])) + po_item_rows = list(set(d.po_detail for d in item_list)) if po_item_rows: purchase_receipts = frappe.db.sql(""" 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 928b373eff..2e794da842 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 @@ -23,7 +23,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum if item_list: itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency) - mode_of_payments = get_mode_of_payments(set([d.parent for d in item_list])) + mode_of_payments = get_mode_of_payments(set(d.parent for d in item_list)) so_dn_map = get_delivery_notes_against_sales_order(item_list) data = [] diff --git a/erpnext/accounts/report/pos_register/pos_register.py b/erpnext/accounts/report/pos_register/pos_register.py index cfbd7fd0c8..6a42bb4fb6 100644 --- a/erpnext/accounts/report/pos_register/pos_register.py +++ b/erpnext/accounts/report/pos_register/pos_register.py @@ -77,14 +77,14 @@ def get_pos_entries(filters, group_by_field): ), filters, as_dict=1) def concat_mode_of_payments(pos_entries): - mode_of_payments = get_mode_of_payments(set([d.pos_invoice for d in pos_entries])) + mode_of_payments = get_mode_of_payments(set(d.pos_invoice for d in pos_entries)) for entry in pos_entries: if mode_of_payments.get(entry.pos_invoice): entry.mode_of_payment = ", ".join(mode_of_payments.get(entry.pos_invoice, [])) def add_subtotal_row(data, group_invoices, group_by_field, group_by_value): - grand_total = sum([d.grand_total for d in group_invoices]) - paid_amount = sum([d.paid_amount for d in group_invoices]) + grand_total = sum(d.grand_total for d in group_invoices) + paid_amount = sum(d.paid_amount for d in group_invoices) data.append({ group_by_field: group_by_value, "grand_total": grand_total, diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index 8ac749d629..10edd41aa8 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -26,7 +26,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum 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) - suppliers = list(set([d.supplier for d in invoice_list])) + suppliers = list(set(d.supplier for d in invoice_list)) supplier_details = get_supplier_details(suppliers) company_currency = frappe.get_cached_value('Company', filters.company, "default_currency") @@ -120,13 +120,13 @@ def get_columns(invoice_list, additional_table_columns): and docstatus = 1 and (account_head is not null and account_head != '') and category in ('Total', 'Valuation and Total') and parent in (%s) order by account_head""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + ', '.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])) + ', '.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] @@ -208,7 +208,7 @@ def get_invoice_expense_map(invoice_list): from `tabPurchase Invoice Item` where parent in (%s) group by parent, expense_account - """ % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + """ % ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1) invoice_expense_map = {} for d in expense_details: @@ -221,7 +221,7 @@ 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) + ', '.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: @@ -238,7 +238,7 @@ def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts): where parent in (%s) and category in ('Total', 'Valuation and Total') and base_tax_amount_after_discount_amount != 0 group by parent, account_head, add_deduct_tax - """ % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + """ % ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1) invoice_tax_map = {} for d in tax_details: @@ -258,7 +258,7 @@ def get_invoice_po_pr_map(invoice_list): select parent, purchase_order, purchase_receipt, po_detail, project from `tabPurchase Invoice Item` where parent in (%s) - """ % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + """ % ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1) invoice_po_pr_map = {} for d in pi_items: diff --git a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py index c234da0fe3..ff774681a2 100644 --- a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py @@ -158,7 +158,7 @@ def get_sales_invoice_data(filters): def get_mode_of_payments(filters): mode_of_payments = {} invoice_list = get_invoices(filters) - invoice_list_names = ",".join(['"' + invoice['name'] + '"' for invoice in invoice_list]) + invoice_list_names = ",".join('"' + invoice['name'] + '"' for invoice in invoice_list) if invoice_list: inv_mop = frappe.db.sql("""select a.owner,a.posting_date, ifnull(b.mode_of_payment, '') as mode_of_payment from `tabSales Invoice` a, `tabSales Invoice Payment` b @@ -197,7 +197,7 @@ def get_invoices(filters): def get_mode_of_payment_details(filters): mode_of_payment_details = {} invoice_list = get_invoices(filters) - invoice_list_names = ",".join(['"' + invoice['name'] + '"' for invoice in invoice_list]) + invoice_list_names = ",".join('"' + invoice['name'] + '"' for invoice in invoice_list) if invoice_list: inv_mop_detail = frappe.db.sql("""select a.owner, a.posting_date, ifnull(b.mode_of_payment, '') as mode_of_payment, sum(b.base_amount) as paid_amount diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index cb2c98b64a..909959323f 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -248,19 +248,19 @@ def get_columns(invoice_list, additional_table_columns): income_accounts = frappe.db.sql_list("""select distinct income_account from `tabSales Invoice Item` where docstatus = 1 and parent in (%s) order by income_account""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list)) tax_accounts = frappe.db.sql_list("""select distinct account_head from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice' and docstatus = 1 and base_tax_amount_after_discount_amount != 0 and parent in (%s) order by account_head""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + ', '.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])) + ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list)) for account in income_accounts: income_columns.append({ @@ -406,7 +406,7 @@ def get_invoices(filters, additional_query_columns): def get_invoice_income_map(invoice_list): income_details = frappe.db.sql("""select parent, income_account, sum(base_net_amount) as amount from `tabSales Invoice Item` where parent in (%s) group by parent, income_account""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1) invoice_income_map = {} for d in income_details: @@ -419,7 +419,7 @@ 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) + ', '.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: @@ -432,7 +432,7 @@ 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 from `tabSales Taxes and Charges` where parent in (%s) group by parent, account_head""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1) invoice_tax_map = {} for d in tax_details: @@ -451,7 +451,7 @@ def get_invoice_so_dn_map(invoice_list): si_items = frappe.db.sql("""select parent, sales_order, delivery_note, so_detail from `tabSales Invoice Item` where parent in (%s) and (ifnull(sales_order, '') != '' or ifnull(delivery_note, '') != '')""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1) invoice_so_dn_map = {} for d in si_items: @@ -475,7 +475,7 @@ def get_invoice_cc_wh_map(invoice_list): si_items = frappe.db.sql("""select parent, cost_center, warehouse from `tabSales Invoice Item` where parent in (%s) and (ifnull(cost_center, '') != '' or ifnull(warehouse, '') != '')""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1) invoice_cc_wh_map = {} for d in si_items: diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py index a8280c1b18..e15715dccd 100644 --- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py +++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py @@ -78,7 +78,7 @@ def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date, f and company=%s and posting_date between %s and %s """, (supplier, company, from_date, to_date), as_dict=1) - supplier_credit_amount = flt(sum([d.credit for d in entries])) + supplier_credit_amount = flt(sum(d.credit for d in entries)) vouchers = [d.voucher_no for d in entries] vouchers += get_advance_vouchers([supplier], company=company, @@ -91,7 +91,7 @@ def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date, f from `tabGL Entry` where account=%s and posting_date between %s and %s and company=%s and credit > 0 and voucher_no in ({0}) - """.format(', '.join(["'%s'" % d for d in vouchers])), + """.format(', '.join("'%s'" % d for d in vouchers)), (account, from_date, to_date, company))[0][0]) date_range_filter = [fiscal_year, from_date, to_date] diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py index b020d0a506..ba461edaf8 100644 --- a/erpnext/accounts/report/utils.py +++ b/erpnext/accounts/report/utils.py @@ -139,6 +139,6 @@ def get_invoiced_item_gross_margin(sales_invoice=None, item_code=None, company=N gross_profit_data = GrossProfitGenerator(filters) result = gross_profit_data.grouped_data if not with_item_data: - result = sum([d.gross_profit for d in result]) + result = sum(d.gross_profit for d in result) return result diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 5a64e27ccb..66a9b60125 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -635,7 +635,7 @@ def get_held_invoices(party_type, party): 'select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()', as_dict=1 ) - held_invoices = set([d['name'] for d in held_invoices]) + held_invoices = set(d['name'] for d in held_invoices) return held_invoices diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 3cd4b802c1..8845f24d10 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -470,7 +470,7 @@ class TestAsset(unittest.TestCase): }) asset.insert() accumulated_depreciation_after_full_schedule = \ - max([d.accumulated_depreciation_amount for d in asset.get("schedules")]) + max(d.accumulated_depreciation_amount for d in asset.get("schedules")) asset_value_after_full_schedule = (flt(asset.gross_purchase_amount) - flt(accumulated_depreciation_after_full_schedule)) 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 14308277c1..2f6b5ee2dc 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -92,7 +92,7 @@ class AssetValueAdjustment(Document): d.value_after_depreciation = asset_value if d.depreciation_method in ("Straight Line", "Manual"): - end_date = max([s.schedule_date for s in asset.schedules if cint(s.finance_book_id) == d.idx]) + end_date = max(s.schedule_date for s in asset.schedules if cint(s.finance_book_id) == d.idx) total_days = date_diff(end_date, self.date) rate_per_day = flt(d.value_after_depreciation) / flt(total_days) from_date = self.date diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 782593a5c5..2629ba7d61 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -139,7 +139,7 @@ class PurchaseOrder(BuyingController): def validate_minimum_order_qty(self): if not self.get("items"): return - items = list(set([d.item_code for d in self.get("items")])) + items = list(set(d.item_code for d in self.get("items"))) itemwise_min_order_qty = frappe._dict(frappe.db.sql("""select name, min_order_qty from tabItem where name in ({0})""".format(", ".join(["%s"] * len(items))), items)) @@ -326,10 +326,10 @@ class PurchaseOrder(BuyingController): so.notify_update() def has_drop_ship_item(self): - return any([d.delivered_by_supplier for d in self.items]) + return any(d.delivered_by_supplier for d in self.items) def is_against_so(self): - return any([d.sales_order for d in self.items if d.sales_order]) + return any(d.sales_order for d in self.items if d.sales_order) def set_received_qty_for_drop_ship_items(self): for item in self.items: diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 39171960d8..3b9f8e9775 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -359,7 +359,7 @@ class TestPurchaseOrder(unittest.TestCase): update_child_qty_rate('Purchase Order', trans_item, po.name) po.reload() - total_reqd_qty_after_change = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")]) + total_reqd_qty_after_change = sum(d.get("required_qty") for d in po.as_dict().get("supplied_items")) self.assertEqual(total_reqd_qty_after_change, 2 * total_reqd_qty) 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 180ba93666..0127eb8163 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -391,7 +391,7 @@ def get_item_from_material_requests_based_on_supplier(source_name, target_doc = def get_supplier_tag(): if not frappe.cache().hget("Supplier", "Tags"): filters = {"document_type": "Supplier"} - tags = list(set([tag.tag for tag in frappe.get_all("Tag Link", filters=filters, fields=["tag"]) if tag])) + tags = list(set(tag.tag for tag in frappe.get_all("Tag Link", filters=filters, fields=["tag"]) if tag)) frappe.cache().hset("Supplier", "Tags", tags) return frappe.cache().hget("Supplier", "Tags") diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 53ded33b6f..7c6061defa 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -610,8 +610,8 @@ class AccountsController(TransactionBase): order_field = "purchase_order" order_doctype = "Purchase Order" - order_list = list(set([d.get(order_field) - for d in self.get("items") if d.get(order_field)])) + order_list = list(set(d.get(order_field) + for d in self.get("items") if d.get(order_field))) journal_entries = get_advance_journal_entries(party_type, party, party_account, amount_field, order_doctype, order_list, include_unallocated) @@ -635,8 +635,8 @@ class AccountsController(TransactionBase): def validate_advance_entries(self): order_field = "sales_order" if self.doctype == "Sales Invoice" else "purchase_order" - order_list = list(set([d.get(order_field) - for d in self.get("items") if d.get(order_field)])) + order_list = list(set(d.get(order_field) + for d in self.get("items") if d.get(order_field))) if not order_list: return diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 3f2d3390c0..da819119b1 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -181,8 +181,8 @@ class BuyingController(StockController): stock_and_asset_items_amount += flt(d.base_net_amount) last_item_idx = d.idx - total_valuation_amount = sum([flt(d.base_tax_amount_after_discount_amount) for d in self.get("taxes") - if d.category in ["Valuation", "Valuation and Total"]]) + total_valuation_amount = sum(flt(d.base_tax_amount_after_discount_amount) for d in self.get("taxes") + if d.category in ["Valuation", "Valuation and Total"]) valuation_amount_adjustment = total_valuation_amount for i, item in enumerate(self.get("items")): @@ -325,7 +325,7 @@ class BuyingController(StockController): def update_raw_materials_supplied_based_on_stock_entries(self): self.set('supplied_items', []) - purchase_orders = set([d.purchase_order for d in self.items]) + purchase_orders = set(d.purchase_order for d in self.items) # qty of raw materials backflushed (for each item per purchase order) backflushed_raw_materials_map = get_backflushed_subcontracted_raw_materials(purchase_orders) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 81ac234e70..7bd739a6ad 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -88,7 +88,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): fields = get_fields("Customer", fields) searchfields = frappe.get_meta("Customer").get_search_fields() - searchfields = " or ".join([field + " like %(txt)s" for field in searchfields]) + searchfields = " or ".join(field + " like %(txt)s" for field in searchfields) return frappe.db.sql("""select {fields} from `tabCustomer` where docstatus < 2 diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 54156f379c..7f28289760 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -428,7 +428,7 @@ class SellingController(StockController): self.po_no = ', '.join(list(set(x.strip() for x in ','.join(po_nos).split(',')))) def get_po_nos(self, ref_doctype, ref_fieldname, po_nos): - doc_list = list(set([d.get(ref_fieldname) for d in self.items if d.get(ref_fieldname)])) + doc_list = list(set(d.get(ref_fieldname) for d in self.items if d.get(ref_fieldname))) if doc_list: po_nos += [d.po_no for d in frappe.get_all(ref_doctype, 'po_no', filters = {'name': ('in', doc_list)}) if d.get('po_no')] diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 83d4c33140..943f7aaeb1 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -299,8 +299,8 @@ class StatusUpdater(Document): 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'])]) + 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: diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 0da723d56e..9c29b0076b 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -313,7 +313,7 @@ class StockController(AccountsController): def get_serialized_items(self): serialized_items = [] - item_codes = list(set([d.item_code for d in self.get("items")])) + item_codes = list(set(d.item_code for d in self.get("items"))) if item_codes: serialized_items = frappe.db.sql_list("""select name from `tabItem` where has_serial_no=1 and name in ({})""".format(", ".join(["%s"]*len(item_codes))), @@ -324,8 +324,8 @@ class StockController(AccountsController): def validate_warehouse(self): from erpnext.stock.utils import validate_disabled_warehouse, validate_warehouse_company - warehouses = list(set([d.warehouse for d in - self.get("items") if getattr(d, "warehouse", None)])) + warehouses = list(set(d.warehouse for d in + self.get("items") if getattr(d, "warehouse", None))) target_warehouses = list(set([d.target_warehouse for d in self.get("items") if getattr(d, "target_warehouse", None)])) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 0b4fb3a3ed..2bb83ea7f0 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -378,10 +378,10 @@ class calculate_taxes_and_totals(object): def manipulate_grand_total_for_inclusive_tax(self): # if fully inclusive taxes and diff - if self.doc.get("taxes") and any([cint(t.included_in_print_rate) for t in self.doc.get("taxes")]): + if self.doc.get("taxes") and any(cint(t.included_in_print_rate) for t in self.doc.get("taxes")): last_tax = self.doc.get("taxes")[-1] - non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount) - for d in self.doc.get("taxes") if not d.included_in_print_rate]) + non_inclusive_tax_amount = sum(flt(d.tax_amount_after_discount_amount) + for d in self.doc.get("taxes") if not d.included_in_print_rate) diff = self.doc.total + non_inclusive_tax_amount \ - flt(last_tax.total, last_tax.precision("total")) @@ -521,8 +521,8 @@ class calculate_taxes_and_totals(object): def calculate_total_advance(self): if self.doc.docstatus < 2: - total_allocated_amount = sum([flt(adv.allocated_amount, adv.precision("allocated_amount")) - for adv in self.doc.get("advances")]) + total_allocated_amount = sum(flt(adv.allocated_amount, adv.precision("allocated_amount")) + for adv in self.doc.get("advances")) self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance")) @@ -622,7 +622,7 @@ class calculate_taxes_and_totals(object): if self.doc.doctype == "Sales Invoice" \ and self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \ - and any([d.type == "Cash" for d in self.doc.payments]): + and any(d.type == "Cash" for d in self.doc.payments): grand_total = self.doc.rounded_total or self.doc.grand_total base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total diff --git a/erpnext/controllers/tests/test_mapper.py b/erpnext/controllers/tests/test_mapper.py index 66459fdbf8..7a4b2d3614 100644 --- a/erpnext/controllers/tests/test_mapper.py +++ b/erpnext/controllers/tests/test_mapper.py @@ -26,8 +26,8 @@ class TestMapper(unittest.TestCase): # Assert that all inserted items are present in updated sales order src_items = item_list_1 + item_list_2 + item_list_3 - self.assertEqual(set([d for d in src_items]), - set([d.item_code for d in updated_so.items])) + self.assertEqual(set(d for d in src_items), + set(d.item_code for d in updated_so.items)) def make_quotation(self, item_list, customer): diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py index ecf041efd1..7c072e4fad 100644 --- a/erpnext/controllers/website_list_for_contact.py +++ b/erpnext/controllers/website_list_for_contact.py @@ -113,7 +113,7 @@ def post_process(doctype, data): doc.set_indicator() doc.status_display = ", ".join(doc.status_display) - doc.items_preview = ", ".join([d.item_name for d in doc.items if d.item_name]) + doc.items_preview = ", ".join(d.item_name for d in doc.items if d.item_name) result.append(doc) return result diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index d764db33f8..cdc4518894 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -317,7 +317,7 @@ class JobCard(Document): 'docstatus': ('!=', 2)}, fields = 'sum(transferred_qty) as qty', group_by='operation_id') if job_cards: - qty = min([d.qty for d in job_cards]) + qty = min(d.qty for d in job_cards) doc.db_set('material_transferred_for_manufacturing', qty) diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index d1583f1473..39a6024e2c 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -232,7 +232,7 @@ def get_project(doctype, txt, searchfield, start, page_len, filters): meta = frappe.get_meta(doctype) searchfields = meta.get_search_fields() search_columns = ", " + ", ".join(searchfields) if searchfields else '' - search_cond = " or " + " or ".join([field + " like %(txt)s" for field in searchfields]) + search_cond = " or " + " or ".join(field + " like %(txt)s" for field in searchfields) return frappe.db.sql(""" select name {search_columns} from `tabProject` where %(key)s like %(txt)s diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index a3e4577f90..c8bd80fca0 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -87,8 +87,8 @@ class Timesheet(Document): def set_dates(self): if self.docstatus < 2 and self.time_logs: - start_date = min([getdate(d.from_time) for d in self.time_logs]) - end_date = max([getdate(d.to_time) for d in self.time_logs]) + start_date = min(getdate(d.from_time) for d in self.time_logs) + end_date = max(getdate(d.to_time) for d in self.time_logs) if start_date and end_date: self.start_date = getdate(start_date) diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index 2c7bb49cfb..98dd617f9b 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -122,7 +122,7 @@ def get_report_summary(data): if not data: return None - avg_completion = sum([project.percent_complete for project in data]) / len(data) + avg_completion = sum(project.percent_complete for project in data) / len(data) total = sum([project.total_tasks for project in data]) total_overdue = sum([project.overdue_tasks for project in data]) completed = sum([project.completed_tasks for project in data]) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 51d86ff0bf..818888c0c1 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -75,7 +75,7 @@ class Customer(TransactionBase): self.loyalty_program_tier = customer.loyalty_program_tier if self.sales_team: - if sum([member.allocated_percentage or 0 for member in self.sales_team]) != 100: + if sum(member.allocated_percentage or 0 for member in self.sales_team) != 100: frappe.throw(_("Total contribution percentage should be equal to 100")) def check_customer_group_change(self): diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 246f9234a4..e4f8a47581 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -50,7 +50,7 @@ class Quotation(SellingController): self.customer_name = company_name or lead_name def update_opportunity(self, status): - for opportunity in list(set([d.prevdoc_docname for d in self.get("items")])): + for opportunity in set(d.prevdoc_docname for d in self.get("items")): if opportunity: self.update_opportunity_status(status, opportunity) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index d9e52e1d69..551f715bd5 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -151,7 +151,7 @@ class SalesOrder(SellingController): frappe.db.sql("update `tabOpportunity` set status = %s where name=%s",(flag,enq[0][0])) def update_prevdoc_status(self, flag=None): - for quotation in list(set([d.prevdoc_docname for d in self.get("items")])): + for quotation in set(d.prevdoc_docname for d in self.get("items")): if quotation: doc = frappe.get_doc("Quotation", quotation) if doc.docstatus==2: diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 077538d479..27e023c1e5 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -54,7 +54,7 @@ class Company(NestedSet): def validate_abbr(self): if not self.abbr: - self.abbr = ''.join([c[0] for c in self.company_name.split()]).upper() + self.abbr = ''.join(c[0] for c in self.company_name.split()).upper() self.abbr = self.abbr.strip() @@ -335,7 +335,7 @@ class Company(NestedSet): clear_defaults_cache() def abbreviate(self): - self.abbr = ''.join([c[0].upper() for c in self.company_name.split()]) + self.abbr = ''.join(c[0].upper() for c in self.company_name.split()) def on_trash(self): """ diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index bff806d547..db31d6d699 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -139,7 +139,7 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non # return child item groups if the type is of "Is Group" return get_child_groups_for_list_in_html(item_group, start, limit, search) - child_groups = ", ".join([frappe.db.escape(i[0]) for i in get_child_groups(product_group)]) + child_groups = ", ".join(frappe.db.escape(i[0]) for i in get_child_groups(product_group)) # base query query = """select I.name, I.item_name, I.item_code, I.route, I.image, I.website_image, I.thumbnail, I.item_group, @@ -239,7 +239,7 @@ def get_item_for_list_in_html(context): return frappe.get_template(products_template).render(context) def get_group_item_count(item_group): - child_groups = ", ".join(['"' + i[0] + '"' for i in get_child_groups(item_group)]) + child_groups = ", ".join('"' + i[0] + '"' for i in get_child_groups(item_group)) return frappe.db.sql("""select count(*) from `tabItem` where docstatus = 0 and show_in_website = 1 and (item_group in (%s) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index 8fdda565d2..508e17c340 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -304,7 +304,7 @@ def validate_serial_no_with_batch(serial_nos, item_code): frappe.throw(_("The serial no {0} does not belong to item {1}") .format(get_link_to_form("Serial No", serial_nos[0]), get_link_to_form("Item", item_code))) - serial_no_link = ','.join([get_link_to_form("Serial No", sn) for sn in serial_nos]) + serial_no_link = ','.join(get_link_to_form("Serial No", sn) for sn in serial_nos) message = "Serial Nos" if len(serial_nos) > 1 else "Serial No" frappe.throw(_("There is no batch found against the {0}: {1}") diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index cce51cb9b1..dd31965fac 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -264,7 +264,7 @@ class DeliveryNote(SellingController): """ Validate that if packed qty exists, it should be equal to qty """ - if not any([flt(d.get('packed_qty')) for d in self.get("items")]): + if not any(flt(d.get('packed_qty')) for d in self.get("items")): return has_error = False for d in self.get("items"): diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py index 81e730126e..9ec28d8981 100644 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py @@ -68,7 +68,7 @@ class DeliveryTrip(Document): delete (bool, optional): Defaults to `False`. `True` if driver details need to be emptied, else `False`. """ - delivery_notes = list(set([stop.delivery_note for stop in self.delivery_stops if stop.delivery_note])) + delivery_notes = list(set(stop.delivery_note for stop in self.delivery_stops if stop.delivery_note)) update_fields = { "driver": self.driver, @@ -136,8 +136,8 @@ class DeliveryTrip(Document): # Include last leg in the final distance calculation self.uom = self.default_distance_uom - total_distance = sum([leg.get("distance", {}).get("value", 0.0) - for leg in directions.get("legs")]) # in meters + total_distance = sum(leg.get("distance", {}).get("value", 0.0) + for leg in directions.get("legs")) # in meters self.total_distance = total_distance * self.uom_conversion_factor else: idx += len(route) - 1 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 83109469fc..5df4d8743f 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -78,7 +78,7 @@ class LandedCostVoucher(Document): .format(item.idx, item.item_code)) def set_total_taxes_and_charges(self): - self.total_taxes_and_charges = sum([flt(d.base_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_applicable_charges_on_item(self): if self.get('taxes') and self.distribute_charges_based_on != 'Distribute Manually': @@ -104,15 +104,15 @@ class LandedCostVoucher(Document): based_on = self.distribute_charges_based_on.lower() if based_on != 'distribute manually': - total = sum([flt(d.get(based_on)) for d in self.get("items")]) + total = sum(flt(d.get(based_on)) for d in self.get("items")) else: # consider for proportion while distributing manually - total = sum([flt(d.get('applicable_charges')) for d in self.get("items")]) + total = sum(flt(d.get('applicable_charges')) for d in self.get("items")) if not total: frappe.throw(_("Total {0} for all items is zero, may be you should change 'Distribute Charges Based On'").format(based_on)) - total_applicable_charges = sum([flt(d.applicable_charges) for d in self.get("items")]) + total_applicable_charges = sum(flt(d.applicable_charges) for d in self.get("items")) precision = get_field_precision(frappe.get_meta("Landed Cost Item").get_field("applicable_charges"), currency=frappe.get_cached_value('Company', self.company, "default_currency")) 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 984ae46c66..32b08f60c4 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 @@ -311,7 +311,7 @@ def create_landed_cost_voucher(receipt_document_type, receipt_document, company, def distribute_landed_cost_on_items(lcv): based_on = lcv.distribute_charges_based_on.lower() - total = sum([flt(d.get(based_on)) for d in lcv.get("items")]) + total = sum(flt(d.get(based_on)) for d in lcv.get("items")) for item in lcv.get("items"): item.applicable_charges = flt(item.get(based_on)) * flt(lcv.total_taxes_and_charges) / flt(total) diff --git a/erpnext/stock/doctype/packing_slip/packing_slip.py b/erpnext/stock/doctype/packing_slip/packing_slip.py index 2008bffcd3..4a843e0fde 100644 --- a/erpnext/stock/doctype/packing_slip/packing_slip.py +++ b/erpnext/stock/doctype/packing_slip/packing_slip.py @@ -88,9 +88,9 @@ class PackingSlip(Document): rows = [d.item_code for d in self.get("items")] # also pick custom fields from delivery note - custom_fields = ', '.join(['dni.`{0}`'.format(d.fieldname) + custom_fields = ', '.join('dni.`{0}`'.format(d.fieldname) for d in frappe.get_meta("Delivery Note Item").get_custom_fields() - if d.fieldtype not in no_value_fields]) + if d.fieldtype not in no_value_fields) if custom_fields: custom_fields = ', ' + custom_fields diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 5095a80214..8d9b675bed 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -246,7 +246,7 @@ class TestPurchaseReceipt(unittest.TestCase): pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=500, is_subcontracted="Yes") self.assertEqual(len(pr.get("supplied_items")), 2) - rm_supp_cost = sum([d.amount for d in pr.get("supplied_items")]) + 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() diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 5ecc9f8140..b236f6a999 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -613,7 +613,7 @@ def fetch_serial_numbers(filters, qty, do_not_include=[]): batch_nos = filters.get("batch_no") expiry_date = filters.get("expiry_date") if batch_nos: - batch_no_condition = """and sr.batch_no in ({}) """.format(', '.join(["'%s'" % d for d in batch_nos])) + batch_no_condition = """and sr.batch_no in ({}) """.format(', '.join("'%s'" % d for d in batch_nos)) if expiry_date: batch_join_selection = "LEFT JOIN `tabBatch` batch on sr.batch_no = batch.name " diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 2f76bc7d56..560ceaa917 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -465,7 +465,7 @@ class StockEntry(StockController): """ # Set rate for outgoing items outgoing_items_cost = self.set_rate_for_outgoing_items(reset_outgoing_rate, raise_error_if_no_rate) - finished_item_qty = sum([d.transfer_qty for d in self.items if d.is_finished_item]) + finished_item_qty = sum(d.transfer_qty for d in self.items if d.is_finished_item) # Set basic rate for incoming items for d in self.get('items'): diff --git a/erpnext/stock/report/item_price_stock/item_price_stock.py b/erpnext/stock/report/item_price_stock/item_price_stock.py index 5296211fae..db7498bb21 100644 --- a/erpnext/stock/report/item_price_stock/item_price_stock.py +++ b/erpnext/stock/report/item_price_stock/item_price_stock.py @@ -89,7 +89,7 @@ def get_item_price_qty_data(filters): {conditions}""" .format(conditions=conditions), filters, as_dict=1) - price_list_names = list(set([item.price_list_name for item in item_results])) + price_list_names = list(set(item.price_list_name for item in item_results)) buying_price_map = get_price_map(price_list_names, buying=1) selling_price_map = get_price_map(price_list_names, selling=1) diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py index 2f70523264..2e13aa0b04 100644 --- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py +++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py @@ -66,7 +66,7 @@ def get_consumed_items(condition): purpose is NULL or purpose not in ({}) ) - """.format(', '.join([f"'{p}'" for p in purpose_to_exclude])) + """.format(', '.join(f"'{p}'" for p in purpose_to_exclude)) condition = condition.replace("posting_date", "sle.posting_date") consumed_items = frappe.db.sql(""" diff --git a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py index 276e42ee43..8fffbccab3 100644 --- a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py +++ b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py @@ -141,7 +141,7 @@ def get_stock_ledger_entries(filters, items): return [] item_conditions_sql = ' and sle.item_code in ({})' \ - .format(', '.join([frappe.db.escape(i) for i in items])) + .format(', '.join(frappe.db.escape(i) for i in items)) conditions = get_sle_conditions(filters) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index bbd73e9112..b6a8063189 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -157,7 +157,7 @@ def get_stock_ledger_entries(filters, items): item_conditions_sql = '' if items: item_conditions_sql = ' and sle.item_code in ({})'\ - .format(', '.join([frappe.db.escape(i, percent=False) for i in items])) + .format(', '.join(frappe.db.escape(i, percent=False) for i in items)) conditions = get_conditions(filters) @@ -253,7 +253,7 @@ def get_items(filters): def get_item_details(items, sle, filters): item_details = {} if not items: - items = list(set([d.item_code for d in sle])) + items = list(set(d.item_code for d in sle)) if not items: return item_details @@ -291,7 +291,7 @@ def get_item_reorder_details(items): select parent, warehouse, warehouse_reorder_qty, warehouse_reorder_level from `tabItem Reorder` where parent in ({0}) - """.format(', '.join([frappe.db.escape(i, percent=False) for i in items])), as_dict=1) + """.format(', '.join(frappe.db.escape(i, percent=False) for i in items)), as_dict=1) return dict((d.parent + d.warehouse, d) for d in item_reorder_details) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index 36996e9674..8909f217f4 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -115,7 +115,7 @@ def get_stock_ledger_entries(filters, items): item_conditions_sql = '' if items: item_conditions_sql = 'and sle.item_code in ({})'\ - .format(', '.join([frappe.db.escape(i) for i in items])) + .format(', '.join(frappe.db.escape(i) for i in items)) sl_entries = frappe.db.sql(""" SELECT @@ -169,7 +169,7 @@ def get_items(filters): def get_item_details(items, sl_entries, include_uom): item_details = {} if not items: - items = list(set([d.item_code for d in sl_entries])) + items = list(set(d.item_code for d in sl_entries)) if not items: return item_details diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index b2825fc26f..fc82c789cc 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -521,7 +521,7 @@ class update_entries_after(object): fields=["purchase_rate", "name", "company"], filters = {'name': ('in', serial_nos)}) - incoming_values = sum([flt(d.purchase_rate) for d in all_serial_nos if d.company==sle.company]) + incoming_values = sum(flt(d.purchase_rate) for d in all_serial_nos if d.company==sle.company) # Get rate for serial nos which has been transferred to other company invalid_serial_nos = [d.name for d in all_serial_nos if d.company!=sle.company] diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.py b/erpnext/support/doctype/warranty_claim/warranty_claim.py index a3428a25af..a20e7a801b 100644 --- a/erpnext/support/doctype/warranty_claim/warranty_claim.py +++ b/erpnext/support/doctype/warranty_claim/warranty_claim.py @@ -29,7 +29,7 @@ class WarrantyClaim(TransactionBase): where t2.parent = t1.name and t2.prevdoc_docname = %s and t1.docstatus!=2""", (self.name)) if lst: - lst1 = ','.join([x[0] for x in lst]) + lst1 = ','.join(x[0] for x in lst) frappe.throw(_("Cancel Material Visit {0} before cancelling this Warranty Claim").format(lst1)) else: frappe.db.set(self, 'status', 'Cancelled') diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 66d6cd3888..70b41767d6 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -131,6 +131,6 @@ def get_non_stock_item_status(item_code, item_warehouse_field): if frappe.db.exists("Product Bundle", item_code): items = frappe.get_doc("Product Bundle", item_code).get_all_children() bundle_warehouse = frappe.db.get_value('Item', item_code, item_warehouse_field) - return all([ get_qty_in_stock(d.item_code, item_warehouse_field, bundle_warehouse).in_stock for d in items ]) + return all(get_qty_in_stock(d.item_code, item_warehouse_field, bundle_warehouse).in_stock for d in items) else: return 1 diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index f99da58e46..db997263c1 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -147,7 +147,7 @@ class TransactionBase(StatusUpdater): if hasattr(self, "prev_link_mapper") and self.prev_link_mapper.get(for_doctype): fieldname = self.prev_link_mapper[for_doctype]["fieldname"] - values = filter(None, tuple([item.as_dict()[fieldname] for item in self.items])) + values = filter(None, tuple(item.as_dict()[fieldname] for item in self.items)) if values: ret = { @@ -180,7 +180,7 @@ def validate_uom_is_integer(doc, uom_field, qty_fields, child_dt=None): if isinstance(qty_fields, string_types): qty_fields = [qty_fields] - distinct_uoms = list(set([d.get(uom_field) for d in doc.get_all_children()])) + distinct_uoms = list(set(d.get(uom_field) for d in doc.get_all_children())) integer_uoms = list(filter(lambda uom: frappe.db.get_value("UOM", uom, "must_be_whole_number", cache=True) or None, distinct_uoms)) From c4d851e45f591667a33e3fb7e50c5bf1cf14a993 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 11 Jun 2021 17:27:43 +0530 Subject: [PATCH 132/180] fix: Test --- ...tracted_raw_materials_to_be_transferred.py | 68 ++++++++++++++----- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py index c1fc6fb82f..11ec7669b0 100644 --- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py +++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py @@ -12,34 +12,68 @@ import json, frappe, unittest class TestSubcontractedItemToBeTransferred(unittest.TestCase): def test_pending_and_transferred_qty(self): - po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes') + po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes', supplier_warehouse="_Test Warehouse 1 - _TC") + + # Material Receipt of RMs make_stock_entry(item_code='_Test Item', target='_Test Warehouse - _TC', qty=100, basic_rate=100) make_stock_entry(item_code='_Test Item Home Desktop 100', target='_Test Warehouse - _TC', qty=100, basic_rate=100) - transfer_subcontracted_raw_materials(po.name) - col, data = execute(filters=frappe._dict({'supplier': po.supplier, - 'from_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=-10)), - 'to_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=10))})) - self.assertEqual(data[0]['purchase_order'], po.name) - self.assertIn(data[0]['rm_item_code'], ['_Test Item', '_Test Item Home Desktop 100']) - self.assertIn(data[0]['p_qty'], [9, 18]) - self.assertIn(data[0]['t_qty'], [1, 2]) + + se = transfer_subcontracted_raw_materials(po) + + col, data = execute(filters=frappe._dict( + { + 'supplier': po.supplier, + 'from_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=-10)), + 'to_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=10)) + } + )) + po.reload() + + po_data = [row for row in data if row.get('purchase_order') == po.name] + + self.assertEqual(len(po_data), 2) + self.assertIn(po_data[0]['rm_item_code'], ['_Test Item', '_Test Item Home Desktop 100']) + self.assertIn(po_data[0]['p_qty'], [9, 18]) + self.assertIn(po_data[0]['transferred_qty'], [1, 2]) self.assertEqual(data[1]['purchase_order'], po.name) self.assertIn(data[1]['rm_item_code'], ['_Test Item', '_Test Item Home Desktop 100']) self.assertIn(data[1]['p_qty'], [9, 18]) - self.assertIn(data[1]['t_qty'], [1, 2]) + self.assertIn(data[1]['transferred_qty'], [1, 2]) + se.cancel() + po.cancel() def transfer_subcontracted_raw_materials(po): rm_item = [ - {'item_code': '_Test Item', 'rm_item_code': '_Test Item', 'item_name': '_Test Item', 'qty': 1, - 'warehouse': '_Test Warehouse - _TC', 'rate': 100, 'amount': 100, 'stock_uom': 'Nos'}, - {'item_code': '_Test Item Home Desktop 100', 'rm_item_code': '_Test Item Home Desktop 100', 'item_name': '_Test Item Home Desktop 100', 'qty': 2, - 'warehouse': '_Test Warehouse - _TC', 'rate': 100, 'amount': 200, 'stock_uom': 'Nos'}] + { + 'name': po.supplied_items[0].name, + 'item_code': '_Test Item Home Desktop 100', + 'rm_item_code': '_Test Item Home Desktop 100', + 'item_name': '_Test Item Home Desktop 100', + 'qty': 2, + 'warehouse': '_Test Warehouse - _TC', + 'rate': 100, + 'amount': 200, + 'stock_uom': 'Nos' + }, + { + 'name': po.supplied_items[1].name, + 'item_code': '_Test Item', + 'rm_item_code': '_Test Item', + 'item_name': '_Test Item', + 'qty': 1, + 'warehouse': '_Test Warehouse - _TC', + 'rate': 100, + 'amount': 100, + 'stock_uom': 'Nos' + } + ] rm_item_string = json.dumps(rm_item) - se = frappe.get_doc(make_rm_stock_entry(po, rm_item_string)) - se.from_warehouse = '_Test Warehouse 1 - _TC' - se.to_warehouse = '_Test Warehouse 1 - _TC' + se = frappe.get_doc(make_rm_stock_entry(po.name, rm_item_string)) + se.from_warehouse = '_Test Warehouse - _TC' + se.to_warehouse = '_Test Warehouse - _TC' se.stock_entry_type = 'Send to Subcontractor' se.save() se.submit() + return se From 400205cc8a9632614617af2f21d598491db8cde8 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Sat, 12 Jun 2021 12:30:53 +0530 Subject: [PATCH 133/180] fix: payroll entry employee detail issue (#25968) * fix: payroll entry employee detail issue * fix: review changes --- erpnext/payroll/doctype/payroll_entry/payroll_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index 3953b463f1..7a70679db4 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -665,6 +665,8 @@ def get_employee_list(filters): emp_list = remove_payrolled_employees(emp_list, filters.start_date, filters.end_date) return emp_list + return [] + @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def employee_query(doctype, txt, searchfield, start, page_len, filters): From 17550fb4bfb968ea5c0be9ab1e233ac55ed35936 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sat, 12 Jun 2021 13:33:21 +0530 Subject: [PATCH 134/180] feat: add Inactive status to Employee --- erpnext/accounts/party.py | 2 +- erpnext/hr/doctype/employee/employee.json | 4 ++-- erpnext/hr/doctype/employee/employee.py | 4 ++-- erpnext/hr/doctype/employee/employee_list.js | 2 +- erpnext/hr/doctype/employee_promotion/employee_promotion.py | 6 +++--- erpnext/hr/doctype/employee_transfer/employee_transfer.py | 6 +++--- erpnext/payroll/doctype/retention_bonus/retention_bonus.py | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index e01cb6e151..e025fc6905 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -457,7 +457,7 @@ def validate_party_frozen_disabled(party_type, party_name): frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen) elif party_type == "Employee": - if frappe.db.get_value("Employee", party_name, "status") == "Left": + if frappe.db.get_value("Employee", party_name, "status") != "Active": frappe.msgprint(_("{0} {1} is not active").format(party_type, party_name), alert=True) def get_timeline_data(doctype, name): diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index 5123d6a5a7..5442ed56c3 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -207,7 +207,7 @@ "label": "Status", "oldfieldname": "status", "oldfieldtype": "Select", - "options": "Active\nLeft", + "options": "Active\nInactive\nLeft", "reqd": 1, "search_index": 1 }, @@ -813,7 +813,7 @@ "idx": 24, "image_field": "image", "links": [], - "modified": "2021-01-02 16:54:33.477439", + "modified": "2021-06-12 11:31:37.730760", "modified_by": "Administrator", "module": "HR", "name": "Employee", diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index ed7d588434..bc5694226a 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -37,7 +37,7 @@ class Employee(NestedSet): def validate(self): from erpnext.controllers.status_updater import validate_status - validate_status(self.status, ["Active", "Temporary Leave", "Left"]) + validate_status(self.status, ["Active", "Inactive", "Left"]) self.employee = self.name self.set_employee_name() @@ -478,7 +478,7 @@ def get_employee_emails(employee_list): @frappe.whitelist() def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False): - filters = [['status', '!=', 'Left']] + filters = [['status', '=', 'Active']] if company and company != 'All Companies': filters.append(['company', '=', company]) diff --git a/erpnext/hr/doctype/employee/employee_list.js b/erpnext/hr/doctype/employee/employee_list.js index 44837030be..6679e318c2 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": "gray"}[doc.status]; + indicator[1] = {"Active": "green", "Inactive": "red", "Left": "gray"}[doc.status]; return indicator; } }; diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.py b/erpnext/hr/doctype/employee_promotion/employee_promotion.py index 4994921268..83fb235f92 100644 --- a/erpnext/hr/doctype/employee_promotion/employee_promotion.py +++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.py @@ -11,12 +11,12 @@ from erpnext.hr.utils import update_employee class EmployeePromotion(Document): def validate(self): - if frappe.get_value("Employee", self.employee, "status") == "Left": - frappe.throw(_("Cannot promote Employee with status Left")) + if frappe.get_value("Employee", self.employee, "status") != "Active": + frappe.throw(_("Cannot promote Employee with status Left or Inactive")) def before_submit(self): if getdate(self.promotion_date) > getdate(): - frappe.throw(_("Employee Promotion cannot be submitted before Promotion Date "), + frappe.throw(_("Employee Promotion cannot be submitted before Promotion Date"), frappe.DocstatusTransitionError) def on_submit(self): diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py index 3539970a32..6eec9fa12a 100644 --- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py +++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py @@ -11,12 +11,12 @@ from erpnext.hr.utils import update_employee class EmployeeTransfer(Document): def validate(self): - if frappe.get_value("Employee", self.employee, "status") == "Left": - frappe.throw(_("Cannot transfer Employee with status Left")) + if frappe.get_value("Employee", self.employee, "status") != "Active": + frappe.throw(_("Cannot transfer Employee with status Left or Inactive")) def before_submit(self): if getdate(self.transfer_date) > getdate(): - frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date "), + frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date"), frappe.DocstatusTransitionError) def on_submit(self): diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py index b8e56ae42a..049ea265cc 100644 --- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py +++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py @@ -10,8 +10,8 @@ from frappe.utils import getdate class RetentionBonus(Document): def validate(self): - if frappe.get_value('Employee', self.employee, 'status') == 'Left': - frappe.throw(_('Cannot create Retention Bonus for left Employees')) + if frappe.get_value('Employee', self.employee, 'status') != 'Active': + frappe.throw(_('Cannot create Retention Bonus for Left or Inactive Employees')) if getdate(self.bonus_payment_date) < getdate(): frappe.throw(_('Bonus Payment Date cannot be a past date')) From cf349aadf9ea7108bb0a95b86da23ffab87d62e5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sat, 12 Jun 2021 14:05:12 +0530 Subject: [PATCH 135/180] fix: unable to enter score in Assessment Result details grid (#25945) (#26031) --- .../education/doctype/assessment_result/assessment_result.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/education/doctype/assessment_result/assessment_result.js b/erpnext/education/doctype/assessment_result/assessment_result.js index 617a873b82..c35f607a99 100644 --- a/erpnext/education/doctype/assessment_result/assessment_result.js +++ b/erpnext/education/doctype/assessment_result/assessment_result.js @@ -6,7 +6,8 @@ frappe.ui.form.on('Assessment Result', { if (!frm.doc.__islocal) { frm.trigger('setup_chart'); } - frm.set_df_property('details', 'read_only', 1); + + frm.get_field('details').grid.cannot_add_rows = true; frm.set_query('course', function() { return { From 28cdff10cfe981c6e91b1a9ef897638e7c800af6 Mon Sep 17 00:00:00 2001 From: Anoop <3326959+akurungadam@users.noreply.github.com> Date: Sat, 12 Jun 2021 14:17:04 +0530 Subject: [PATCH 136/180] fix: update linked Customer on Patient update only if Link Customer to Patient is enabled (#25926) --- erpnext/healthcare/doctype/patient/patient.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py index 789d452c07..cebcb2068e 100644 --- a/erpnext/healthcare/doctype/patient/patient.py +++ b/erpnext/healthcare/doctype/patient/patient.py @@ -33,21 +33,21 @@ class Patient(Document): self.reload() # self.notify_update() def on_update(self): - if self.customer: - customer = frappe.get_doc('Customer', self.customer) - if self.customer_group: - customer.customer_group = self.customer_group - if self.territory: - customer.territory = self.territory + if frappe.db.get_single_value('Healthcare Settings', 'link_customer_to_patient'): + if self.customer: + customer = frappe.get_doc('Customer', self.customer) + if self.customer_group: + customer.customer_group = self.customer_group + if self.territory: + customer.territory = self.territory - customer.customer_name = self.patient_name - customer.default_price_list = self.default_price_list - customer.default_currency = self.default_currency - customer.language = self.language - customer.ignore_mandatory = True - customer.save(ignore_permissions=True) - else: - if frappe.db.get_single_value('Healthcare Settings', 'link_customer_to_patient'): + customer.customer_name = self.patient_name + customer.default_price_list = self.default_price_list + customer.default_currency = self.default_currency + customer.language = self.language + customer.ignore_mandatory = True + customer.save(ignore_permissions=True) + else: create_customer(self) def set_full_name(self): From 580346360fc3a6dd168e726cd035f5cc42f8f3c2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 14 Jun 2021 11:16:39 +0530 Subject: [PATCH 137/180] fix: Auto tax calculations in Payment Entry --- .../doctype/payment_entry/payment_entry.js | 86 +++++++++++++++++-- .../doctype/payment_entry/payment_entry.py | 44 +++++----- .../purchase_taxes_and_charges.json | 3 +- .../sales_taxes_and_charges.json | 3 +- erpnext/controllers/accounts_controller.py | 34 ++++---- 5 files changed, 118 insertions(+), 52 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 939f3546ff..d3ac3a6676 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -1087,6 +1087,8 @@ frappe.ui.form.on('Payment Entry', { initialize_taxes: function(frm) { $.each(frm.doc["taxes"] || [], function(i, tax) { + frm.events.validate_taxes_and_charges(tax); + frm.events.validate_inclusive_tax(tax); tax.item_wise_tax_detail = {}; let tax_fields = ["total", "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]; @@ -1101,6 +1103,73 @@ frappe.ui.form.on('Payment Entry', { }); }, + validate_taxes_and_charges: function(d) { + let msg = ""; + + if (d.account_head && !d.description) { + // set description from account head + d.description = d.account_head.split(' - ').slice(0, -1).join(' - '); + } + + if (!d.charge_type && (d.row_id || d.rate || d.tax_amount)) { + msg = __("Please select Charge Type first"); + d.row_id = ""; + d.rate = d.tax_amount = 0.0; + } else if ((d.charge_type == 'Actual' || d.charge_type == 'On Net Total' || d.charge_type == 'On Paid Amount') && d.row_id) { + msg = __("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'"); + d.row_id = ""; + } else if ((d.charge_type == 'On Previous Row Amount' || d.charge_type == 'On Previous Row Total') && d.row_id) { + if (d.idx == 1) { + msg = __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"); + d.charge_type = ''; + } else if (!d.row_id) { + msg = __("Please specify a valid Row ID for row {0} in table {1}", [d.idx, __(d.doctype)]); + d.row_id = ""; + } else if (d.row_id && d.row_id >= d.idx) { + msg = __("Cannot refer row number greater than or equal to current row number for this Charge type"); + d.row_id = ""; + } + } + if (msg) { + frappe.validated = false; + refresh_field("taxes"); + frappe.throw(msg); + } + + }, + + validate_inclusive_tax: function(tax) { + let actual_type_error = function() { + let msg = __("Actual type tax cannot be included in Item rate in row {0}", [tax.idx]) + frappe.throw(msg); + }; + + let on_previous_row_error = function(row_range) { + let msg = __("For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included", + [tax.idx, __(tax.doctype), tax.charge_type, row_range]) + frappe.throw(msg); + }; + + if(cint(tax.included_in_paid_amount)) { + if(tax.charge_type == "Actual") { + // inclusive tax cannot be of type Actual + actual_type_error(); + } else if(tax.charge_type == "On Previous Row Amount" && + !cint(this.frm.doc["taxes"][tax.row_id - 1].included_in_paid_amount) + ) { + // referred row should also be an inclusive tax + on_previous_row_error(tax.row_id); + } else if(tax.charge_type == "On Previous Row Total") { + let taxes_not_included = $.map(this.frm.doc["taxes"].slice(0, tax.row_id), + function(t) { return cint(t.included_in_paid_amount) ? null : t; }); + if(taxes_not_included.length > 0) { + // all rows above this tax should be inclusive + on_previous_row_error(tax.row_id == 1 ? "1" : "1 - " + tax.row_id); + } + } + } + }, + determine_exclusive_rate: function(frm) { let has_inclusive_tax = false; $.each(frm.doc["taxes"] || [], function(i, row) { @@ -1110,8 +1179,7 @@ frappe.ui.form.on('Payment Entry', { let cumulated_tax_fraction = 0.0; $.each(frm.doc["taxes"] || [], function(i, tax) { - let current_tax_fraction = frm.events.get_current_tax_fraction(frm, tax); - tax.tax_fraction_for_current_item = current_tax_fraction[0]; + tax.tax_fraction_for_current_item = frm.events.get_current_tax_fraction(frm, tax); if(i==0) { tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item; @@ -1132,9 +1200,7 @@ frappe.ui.form.on('Payment Entry', { if(cint(tax.included_in_paid_amount)) { let tax_rate = tax.rate; - if (tax.charge_type == "Actual") { - current_tax_fraction = tax.tax_amount/(frm.doc.paid_amount_after_tax + frm.doc.tax_amount); - } else if(tax.charge_type == "On Paid Amount") { + if(tax.charge_type == "On Paid Amount") { current_tax_fraction = (tax_rate / 100.0); } else if(tax.charge_type == "On Previous Row Amount") { current_tax_fraction = (tax_rate / 100.0) * @@ -1147,7 +1213,6 @@ frappe.ui.form.on('Payment Entry', { if(tax.add_deduct_tax && tax.add_deduct_tax == "Deduct") { current_tax_fraction *= -1; - inclusive_tax_amount_per_qty *= -1; } return current_tax_fraction; }, @@ -1207,10 +1272,8 @@ frappe.ui.form.on('Payment Entry', { frappe.throw( __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")); } - if (!tax.row_id) { - tax.row_id = tax.idx - 1; - } } + if(tax.charge_type == "Actual") { current_tax_amount = flt(tax.tax_amount, precision("tax_amount", tax)) } else if(tax.charge_type == "On Paid Amount") { @@ -1296,6 +1359,11 @@ frappe.ui.form.on('Advance Taxes and Charges', { included_in_paid_amount: function(frm) { frm.events.apply_taxes(frm); frm.events.set_unallocated_amount(frm); + }, + + charge_type: function(frm) { + frm.events.apply_taxes(frm); + frm.events.set_unallocated_amount(frm); } }) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 2c6deb3896..70b38735e7 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -16,9 +16,11 @@ from erpnext.accounts.doctype.bank_account.bank_account import get_party_bank_ac from erpnext.controllers.accounts_controller import AccountsController, get_supplier_block_status from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details - from six import string_types, iteritems +from erpnext.controllers.accounts_controller import validate_conversion_rate, \ + validate_taxes_and_charges, validate_inclusive_tax + class InvalidPaymentEntry(ValidationError): pass @@ -407,20 +409,6 @@ class PaymentEntry(AccountsController): net_total = self.paid_amount included_in_paid_amount = 0 - if self.get('references'): - for doc in self.get('references'): - if doc.reference_doctype == 'Purchase Order': - reference_doclist.append(doc.reference_name) - - if reference_doclist: - order_amount = frappe.db.get_all('Purchase Order', fields=['sum(net_total)'], - filters = {'name': ('in', reference_doclist), 'docstatus': 1, - 'apply_tds': 1}, as_list=1) - - if order_amount: - net_total = order_amount[0][0] - included_in_paid_amount = 1 - # Adding args as purchase invoice to get TDS amount args = frappe._dict({ 'company': self.company, @@ -719,9 +707,9 @@ class PaymentEntry(AccountsController): if account_currency != self.company_currency: frappe.throw(_("Currency for {0} must be {1}").format(d.account_head, self.company_currency)) - if (self.payment_type == 'Pay' and self.advance_tax_account) or self.payment_type == 'Receive': + if self.payment_type == 'Pay': dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit" - elif (self.payment_type == 'Receive' and self.advance_tax_account) or self.payment_type == 'Pay': + elif self.payment_type == 'Receive': dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit" payment_or_advance_account = self.get_party_account_for_taxes() @@ -747,6 +735,8 @@ class PaymentEntry(AccountsController): if account_currency==self.company_currency else d.tax_amount, "cost_center": self.cost_center, + "party_type": self.party_type, + "party": self.party }, account_currency, item=d)) def add_deductions_gl_entries(self, gl_entries): @@ -770,9 +760,9 @@ class PaymentEntry(AccountsController): def get_party_account_for_taxes(self): if self.advance_tax_account: return self.advance_tax_account - elif self.payment_type == 'Pay': - return self.paid_from elif self.payment_type == 'Receive': + return self.paid_from + elif self.payment_type == 'Pay': return self.paid_to def update_advance_paid(self): @@ -823,6 +813,9 @@ class PaymentEntry(AccountsController): def initialize_taxes(self): for tax in self.get("taxes"): + validate_taxes_and_charges(tax) + validate_inclusive_tax(tax, self) + tax_fields = ["total", "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"] if tax.charge_type != "Actual": @@ -918,15 +911,11 @@ class PaymentEntry(AccountsController): if cint(tax.included_in_paid_amount): tax_rate = tax.rate - if tax.charge_type == 'Actual': - current_tax_fraction = tax.tax_amount/ (self.paid_amount_after_tax + tax.tax_amount) - elif tax.charge_type == "On Paid Amount": + if tax.charge_type == "On Paid Amount": current_tax_fraction = tax_rate / 100.0 - elif tax.charge_type == "On Previous Row Amount": current_tax_fraction = (tax_rate / 100.0) * \ self.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item - elif tax.charge_type == "On Previous Row Total": current_tax_fraction = (tax_rate / 100.0) * \ self.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item @@ -1626,6 +1615,13 @@ def set_paid_amount_and_received_amount(dt, party_account_currency, bank, outsta paid_amount = received_amount * doc.get('conversion_rate', 1) if dt == "Employee Advance": paid_amount = received_amount * doc.get('exchange_rate', 1) + + if dt == "Purchase Order" and doc.apply_tds: + if party_account_currency == bank.account_currency: + paid_amount = received_amount = doc.base_net_total + else: + paid_amount = received_amount = doc.base_net_total * doc.get('exchange_rate', 1) + return paid_amount, received_amount def apply_early_payment_discount(paid_amount, received_amount, doc): diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json index 9b07645ccc..1fa68e0a8a 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json @@ -218,6 +218,7 @@ }, { "default": "0", + "depends_on": "eval:['Purchase Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)", "description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry", "fieldname": "included_in_paid_amount", "fieldtype": "Check", @@ -227,7 +228,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2021-06-09 11:48:25.335733", + "modified": "2021-06-14 01:43:50.750455", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Taxes and Charges", diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json index 170d34e651..1b7a0fe562 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json @@ -195,6 +195,7 @@ }, { "default": "0", + "depends_on": "eval:['Sales Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)", "description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry", "fieldname": "included_in_paid_amount", "fieldtype": "Check", @@ -205,7 +206,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-06-09 11:48:04.691596", + "modified": "2021-06-14 01:44:36.899147", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 7c6061defa..a507159cb6 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1192,7 +1192,7 @@ def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, c def validate_taxes_and_charges(tax): - if tax.charge_type in ['Actual', 'On Net Total'] and tax.row_id: + if tax.charge_type in ['Actual', 'On Net Total', 'On Paid Amount'] and tax.row_id: frappe.throw(_("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'")) elif tax.charge_type in ['On Previous Row Amount', 'On Previous Row Total']: if cint(tax.idx) == 1: @@ -1209,23 +1209,23 @@ def validate_taxes_and_charges(tax): def validate_inclusive_tax(tax, doc): def _on_previous_row_error(row_range): - throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, - row_range)) + throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range)) - if cint(getattr(tax, "included_in_print_rate", None)): - if tax.charge_type == "Actual": - # inclusive tax cannot be of type Actual - throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate").format(tax.idx)) - elif tax.charge_type == "On Previous Row Amount" and \ - not cint(doc.get("taxes")[cint(tax.row_id) - 1].included_in_print_rate): - # referred row should also be inclusive - _on_previous_row_error(tax.row_id) - elif tax.charge_type == "On Previous Row Total" and \ - not all([cint(t.included_in_print_rate) for t in doc.get("taxes")[:cint(tax.row_id) - 1]]): - # all rows about the reffered tax should be inclusive - _on_previous_row_error("1 - %d" % (tax.row_id,)) - elif tax.get("category") == "Valuation": - frappe.throw(_("Valuation type charges can not be marked as Inclusive")) + for fieldname in ['included_in_print_rate', 'included_in_paid_amount']: + if cint(getattr(tax, fieldname, None)): + if tax.charge_type == "Actual": + # inclusive tax cannot be of type Actual + throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(tax.idx)) + elif tax.charge_type == "On Previous Row Amount" and \ + not cint(doc.get("taxes")[cint(tax.row_id) - 1].get(fieldname)): + # referred row should also be inclusive + _on_previous_row_error(tax.row_id) + elif tax.charge_type == "On Previous Row Total" and \ + not all([cint(t.get(fieldname) for t in doc.get("taxes")[:cint(tax.row_id) - 1])]): + # all rows about the referred tax should be inclusive + _on_previous_row_error("1 - %d" % (cint(tax.row_id),)) + elif tax.get("category") == "Valuation": + frappe.throw(_("Valuation type charges can not be marked as Inclusive")) def set_balance_in_account_currency(gl_dict, account_currency=None, conversion_rate=None, company_currency=None): From 0511ffcf30459a8646978429101c7edf6315f69b Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 14 Jun 2021 13:22:44 +0530 Subject: [PATCH 138/180] fix(General Ledger): Implement multi-account selection --- .../report/general_ledger/general_ledger.js | 15 +++++------- .../report/general_ledger/general_ledger.py | 24 ++++++++++++++----- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 84f786814d..f3c3865b4e 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -36,16 +36,13 @@ frappe.query_reports["General Ledger"] = { { "fieldname":"account", "label": __("Account"), - "fieldtype": "Link", + "fieldtype": "MultiSelectList", "options": "Account", - "get_query": function() { - var company = frappe.query_report.get_filter_value('company'); - return { - "doctype": "Account", - "filters": { - "company": company, - } - } + get_data: function(txt) { + console.log("txt = ", txt) + return frappe.db.get_link_options('Account', txt, { + company: frappe.query_report.get_filter_value("company") + }); } }, { diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 562df4f6f7..53c638bf4a 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -49,8 +49,12 @@ def validate_filters(filters, account_details): if not filters.get("from_date") and not filters.get("to_date"): frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date")))) - if filters.get("account") and not account_details.get(filters.account): - frappe.throw(_("Account {0} does not exists").format(filters.account)) + for account in filters.account: + if not account_details.get(account): + frappe.throw(_("Account {0} does not exists").format(account)) + + if filters.get('account'): + filters.account = frappe.parse_json(filters.get('account')) if (filters.get("account") and filters.get("group_by") == _('Group by Account') and account_details[filters.account].is_group == 0): @@ -87,7 +91,7 @@ def set_account_currency(filters): account_currency = None if filters.get("account"): - account_currency = get_account_currency(filters.account) + account_currency = get_account_currency(filters.account[0]) elif filters.get("party"): gle_currency = frappe.db.get_value( "GL Entry", { @@ -205,10 +209,18 @@ def get_gl_entries(filters, accounting_dimensions): def get_conditions(filters): conditions = [] + if filters.get("account") and not filters.get("include_dimensions"): - lft, rgt = frappe.db.get_value("Account", filters["account"], ["lft", "rgt"]) - conditions.append("""account in (select name from tabAccount - where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt)) + account_conditions = "" + for account in filters["account"]: + lft, rgt = frappe.db.get_value("Account", account, ["lft", "rgt"]) + account_conditions += """account in (select name from tabAccount + where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt) + + # so that the OR doesn't get added to the last account condition + if account != filters["account"][-1]: + account_conditions += " OR " + conditions.append(account_conditions) if filters.get("cost_center"): filters.cost_center = get_cost_centers_with_children(filters.cost_center) From 8718013c96441a7ab71b22bc97fe9bc06bdb01d7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 14 Jun 2021 14:34:44 +0530 Subject: [PATCH 139/180] fix: Add separate function to validate payment entry taxes --- .../doctype/payment_entry/payment_entry.py | 22 ++++++++++++-- erpnext/controllers/accounts_controller.py | 29 +++++++++---------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 70b38735e7..3a1c7b9234 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -18,8 +18,7 @@ from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details from six import string_types, iteritems -from erpnext.controllers.accounts_controller import validate_conversion_rate, \ - validate_taxes_and_charges, validate_inclusive_tax +from erpnext.controllers.accounts_controller import validate_taxes_and_charges class InvalidPaymentEntry(ValidationError): pass @@ -925,6 +924,25 @@ class PaymentEntry(AccountsController): return current_tax_fraction +def validate_inclusive_tax(tax, doc): + def _on_previous_row_error(row_range): + throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range)) + + if cint(getattr(tax, "included_in_paid_amount", None)): + if tax.charge_type == "Actual": + # inclusive tax cannot be of type Actual + throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(tax.idx)) + elif tax.charge_type == "On Previous Row Amount" and \ + not cint(doc.get("taxes")[cint(tax.row_id) - 1].included_in_paid_amount): + # referred row should also be inclusive + _on_previous_row_error(tax.row_id) + elif tax.charge_type == "On Previous Row Total" and \ + not all([cint(t.included_in_paid_amount for t in doc.get("taxes")[:cint(tax.row_id) - 1])]): + # all rows about the referred tax should be inclusive + _on_previous_row_error("1 - %d" % (cint(tax.row_id),)) + elif tax.get("category") == "Valuation": + frappe.throw(_("Valuation type charges can not be marked as Inclusive")) + @frappe.whitelist() def get_outstanding_reference_documents(args): diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a507159cb6..1cd0f5f5b2 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1211,21 +1211,20 @@ def validate_inclusive_tax(tax, doc): def _on_previous_row_error(row_range): throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range)) - for fieldname in ['included_in_print_rate', 'included_in_paid_amount']: - if cint(getattr(tax, fieldname, None)): - if tax.charge_type == "Actual": - # inclusive tax cannot be of type Actual - throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(tax.idx)) - elif tax.charge_type == "On Previous Row Amount" and \ - not cint(doc.get("taxes")[cint(tax.row_id) - 1].get(fieldname)): - # referred row should also be inclusive - _on_previous_row_error(tax.row_id) - elif tax.charge_type == "On Previous Row Total" and \ - not all([cint(t.get(fieldname) for t in doc.get("taxes")[:cint(tax.row_id) - 1])]): - # all rows about the referred tax should be inclusive - _on_previous_row_error("1 - %d" % (cint(tax.row_id),)) - elif tax.get("category") == "Valuation": - frappe.throw(_("Valuation type charges can not be marked as Inclusive")) + if cint(getattr(tax, "included_in_print_rate", None)): + if tax.charge_type == "Actual": + # inclusive tax cannot be of type Actual + throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(tax.idx)) + elif tax.charge_type == "On Previous Row Amount" and \ + not cint(doc.get("taxes")[cint(tax.row_id) - 1].included_in_print_rate): + # referred row should also be inclusive + _on_previous_row_error(tax.row_id) + elif tax.charge_type == "On Previous Row Total" and \ + not all([cint(t.included_in_print_rate for t in doc.get("taxes")[:cint(tax.row_id) - 1])]): + # all rows about the referred tax should be inclusive + _on_previous_row_error("1 - %d" % (cint(tax.row_id),)) + elif tax.get("category") == "Valuation": + frappe.throw(_("Valuation type charges can not be marked as Inclusive")) def set_balance_in_account_currency(gl_dict, account_currency=None, conversion_rate=None, company_currency=None): From 9eac4d0af66ab1952e20d0236d675ff09f03657a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 14 Jun 2021 14:44:19 +0530 Subject: [PATCH 140/180] fix: Import throw --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 3a1c7b9234..b6b2bef963 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, erpnext, json -from frappe import _, scrub, ValidationError +from frappe import _, scrub, ValidationError, throw from frappe.utils import flt, comma_or, nowdate, getdate, cint from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on from erpnext.accounts.party import get_party_account From 3b070d1b0540d320edecceefec2bf51a3e308bb2 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 14 Jun 2021 14:51:33 +0530 Subject: [PATCH 141/180] fix: Flaky test for Report Subcontracted Raw materials to be transferred --- ...tracted_raw_materials_to_be_transferred.py | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py index 11ec7669b0..2448e17c50 100644 --- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py +++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py @@ -30,42 +30,54 @@ class TestSubcontractedItemToBeTransferred(unittest.TestCase): po.reload() po_data = [row for row in data if row.get('purchase_order') == po.name] + # Alphabetically sort to be certain of order + po_data = sorted(po_data, key = lambda i: i['rm_item_code']) self.assertEqual(len(po_data), 2) - self.assertIn(po_data[0]['rm_item_code'], ['_Test Item', '_Test Item Home Desktop 100']) - self.assertIn(po_data[0]['p_qty'], [9, 18]) - self.assertIn(po_data[0]['transferred_qty'], [1, 2]) + self.assertEqual(po_data[0]['purchase_order'], po.name) - self.assertEqual(data[1]['purchase_order'], po.name) - self.assertIn(data[1]['rm_item_code'], ['_Test Item', '_Test Item Home Desktop 100']) - self.assertIn(data[1]['p_qty'], [9, 18]) - self.assertIn(data[1]['transferred_qty'], [1, 2]) + self.assertEqual(po_data[0]['rm_item_code'], '_Test Item') + self.assertEqual(po_data[0]['p_qty'], 8) + self.assertEqual(po_data[0]['transferred_qty'], 2) + + self.assertEqual(po_data[1]['rm_item_code'], '_Test Item Home Desktop 100') + self.assertEqual(po_data[1]['p_qty'], 19) + self.assertEqual(po_data[1]['transferred_qty'], 1) se.cancel() po.cancel() def transfer_subcontracted_raw_materials(po): + # Order of supplied items fetched in PO is flaky + transfer_qty_map = { + '_Test Item': 2, + '_Test Item Home Desktop 100': 1 + } + + item_1 = po.supplied_items[0].rm_item_code + item_2 = po.supplied_items[1].rm_item_code + rm_item = [ { 'name': po.supplied_items[0].name, - 'item_code': '_Test Item Home Desktop 100', - 'rm_item_code': '_Test Item Home Desktop 100', - 'item_name': '_Test Item Home Desktop 100', - 'qty': 2, + 'item_code': item_1, + 'rm_item_code': item_1, + 'item_name': item_1, + 'qty': transfer_qty_map[item_1], 'warehouse': '_Test Warehouse - _TC', 'rate': 100, - 'amount': 200, + 'amount': 100 * transfer_qty_map[item_1], 'stock_uom': 'Nos' }, { 'name': po.supplied_items[1].name, - 'item_code': '_Test Item', - 'rm_item_code': '_Test Item', - 'item_name': '_Test Item', - 'qty': 1, + 'item_code': item_2, + 'rm_item_code': item_2, + 'item_name': item_2, + 'qty': transfer_qty_map[item_2], 'warehouse': '_Test Warehouse - _TC', 'rate': 100, - 'amount': 100, + 'amount': 100 * transfer_qty_map[item_2], 'stock_uom': 'Nos' } ] From 27ec51f021931c6d9505e73e6852cb6c4bb97f86 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 14 Jun 2021 16:41:56 +0530 Subject: [PATCH 142/180] fix(General Ledger): Filter Cost Center drop-down list by Company --- erpnext/accounts/report/general_ledger/general_ledger.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 84f786814d..80a25c907e 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -135,7 +135,9 @@ frappe.query_reports["General Ledger"] = { "label": __("Cost Center"), "fieldtype": "MultiSelectList", get_data: function(txt) { - return frappe.db.get_link_options('Cost Center', txt); + return frappe.db.get_link_options('Cost Center', txt, { + company: frappe.query_report.get_filter_value("company") + }); } }, { From 75b30efb050ed333d519ebad924b4cb188098521 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 14 Jun 2021 16:42:27 +0530 Subject: [PATCH 143/180] fix(General Ledger): Filter Project drop-down list by Company --- erpnext/accounts/report/general_ledger/general_ledger.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 80a25c907e..a8e55307f9 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -145,7 +145,9 @@ frappe.query_reports["General Ledger"] = { "label": __("Project"), "fieldtype": "MultiSelectList", get_data: function(txt) { - return frappe.db.get_link_options('Project', txt); + return frappe.db.get_link_options('Project', txt, { + company: frappe.query_report.get_filter_value("company") + }); } }, { From 4cd0f6ce23f703e2f98baab83ae0b38ac9e26943 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 14 Jun 2021 20:01:04 +0530 Subject: [PATCH 144/180] fix: Revert unintended changes --- erpnext/controllers/accounts_controller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 1cd0f5f5b2..243939b275 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1220,9 +1220,9 @@ def validate_inclusive_tax(tax, doc): # referred row should also be inclusive _on_previous_row_error(tax.row_id) elif tax.charge_type == "On Previous Row Total" and \ - not all([cint(t.included_in_print_rate for t in doc.get("taxes")[:cint(tax.row_id) - 1])]): + not all([cint(t.included_in_print_rate) for t in doc.get("taxes")[:cint(tax.row_id) - 1]]): # all rows about the referred tax should be inclusive - _on_previous_row_error("1 - %d" % (cint(tax.row_id),)) + _on_previous_row_error("1 - %d" % (tax.row_id,)) elif tax.get("category") == "Valuation": frappe.throw(_("Valuation type charges can not be marked as Inclusive")) From b5a14911763fa29ad9f300fdd0dd1b88a2e5126f Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Tue, 15 Jun 2021 12:44:04 +0530 Subject: [PATCH 145/180] fix: escaped warehouse value for sql query (#26049) --- erpnext/controllers/stock_controller.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 9c29b0076b..6a7c9e3d0e 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -558,11 +558,8 @@ def future_sle_exists(args): or_conditions = [] for warehouse, items in warehouse_items_map.items(): or_conditions.append( - "warehouse = '{}' and item_code in ({})".format( - warehouse, - ", ".join(frappe.db.escape(item) for item in items) - ) - ) + f"""warehouse = {frappe.db.escape(warehouse)} + and item_code in ({', '.join(frappe.db.escape(item) for item in items)})""") return frappe.db.sql(""" select name From 79dc0f0afce6df44e88b223f8db7fb947047c710 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 03:37:11 +0530 Subject: [PATCH 146/180] fix: Remove console.log() --- erpnext/accounts/report/general_ledger/general_ledger.js | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index f3c3865b4e..28139d14b2 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -39,7 +39,6 @@ frappe.query_reports["General Ledger"] = { "fieldtype": "MultiSelectList", "options": "Account", get_data: function(txt) { - console.log("txt = ", txt) return frappe.db.get_link_options('Account', txt, { company: frappe.query_report.get_filter_value("company") }); From 53a9ac44662be877160c5acbbafc9fcaaa16bf21 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 04:10:29 +0530 Subject: [PATCH 147/180] fix(General Ledger): Condense account_conditions --- erpnext/accounts/report/general_ledger/general_ledger.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 53c638bf4a..aed9c4a049 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -211,15 +211,16 @@ def get_conditions(filters): conditions = [] if filters.get("account") and not filters.get("include_dimensions"): - account_conditions = "" + account_conditions = "account in (select name from tabAccount where" for account in filters["account"]: lft, rgt = frappe.db.get_value("Account", account, ["lft", "rgt"]) - account_conditions += """account in (select name from tabAccount - where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt) + account_conditions += """ (lft>=%s and rgt<=%s) """ % (lft, rgt) # so that the OR doesn't get added to the last account condition if account != filters["account"][-1]: - account_conditions += " OR " + account_conditions += "OR" + + account_conditions += "and docstatus<2)" conditions.append(account_conditions) if filters.get("cost_center"): From 1fd80992d705f55215a89a89a90e445224937e1b Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 16 Jun 2021 13:27:34 +0530 Subject: [PATCH 148/180] fix(pos): 'NoneType' object is not iterable (#26066) --- erpnext/selling/page/point_of_sale/point_of_sale.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index 7742f24385..296c8c2fd9 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -9,7 +9,7 @@ from erpnext.accounts.doctype.pos_profile.pos_profile import get_item_groups from erpnext.accounts.doctype.pos_invoice.pos_invoice import get_stock_availability def search_by_term(search_term, warehouse, price_list): - result = search_for_serial_or_batch_or_barcode_number(search_term) + result = search_for_serial_or_batch_or_barcode_number(search_term) or {} item_code = result.get("item_code") or search_term serial_no = result.get("serial_no") or "" @@ -23,9 +23,9 @@ def search_by_term(search_term, warehouse, price_list): item_stock_qty = get_stock_availability(item_code, warehouse) price_list_rate, currency = frappe.db.get_value('Item Price', { - 'price_list': price_list, - 'item_code': item_code - }, ["price_list_rate", "currency"]) + 'price_list': price_list, + 'item_code': item_code + }, ["price_list_rate", "currency"]) or [None, None] item_info.update({ 'serial_no': serial_no, @@ -46,7 +46,7 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_te result = [] if search_term: - result = search_by_term(search_term, warehouse, price_list) + result = search_by_term(search_term, warehouse, price_list) or [] if result: return result From b1c72da7d7511dbdf865f8014984f47c6b6d6849 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 16 Jun 2021 14:08:32 +0530 Subject: [PATCH 149/180] fix: Training event --- .../training_scheduled.json | 4 ++-- .../training_scheduled/training_scheduled.md | 22 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.json b/erpnext/hr/notification/training_scheduled/training_scheduled.json index e49541e321..f3650038fd 100644 --- a/erpnext/hr/notification/training_scheduled/training_scheduled.json +++ b/erpnext/hr/notification/training_scheduled/training_scheduled.json @@ -11,8 +11,8 @@ "event": "Submit", "idx": 0, "is_standard": 1, - "message": "\n \n \n \n \n \n \n \n
\n
\n {{_(\"Training Event:\")}} {{ doc.event_name }}\n
\n
\n\n\n \n \n \n \n \n \n \n
\n
\n {{ doc.introduction }}\n
    \n
  • {{_(\"Event Location\")}}: {{ doc.location }}
  • \n {% set start = frappe.utils.get_datetime(doc.start_time) %}\n {% set end = frappe.utils.get_datetime(doc.end_time) %}\n {% if start.date() == end.date() %}\n
  • {{_(\"Date\")}}: {{ start.strftime(\"%A, %d %b %Y\") }}
  • \n
  • \n {{_(\"Timing\")}}: {{ start.strftime(\"%I:%M %p\") + ' to ' + end.strftime(\"%I:%M %p\") }}\n
  • \n {% else %}\n
  • {{_(\"Start Time\")}}: {{ start.strftime(\"%A, %d %b %Y at %I:%M %p\") }}\n
  • \n
  • {{_(\"End Time\")}}: {{ end.strftime(\"%A, %d %b %Y at %I:%M %p\") }}\n
  • \n {% endif %}\n
  • {{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • \n {% if doc.is_mandatory %}\n
  • Note: This Training Event is mandatory
  • \n {% endif %}\n
\n
\n
", - "modified": "2021-05-24 16:29:13.165930", + "message": "\n \n \n \n \n \n \n \n
\n
\n {{_(\"Training Event:\")}} {{ doc.event_name }}\n
\n
\n\n\n \n \n \n \n \n \n \n
\n
\n {{ doc.introduction }}\n
    \n
  • {{_(\"Event Location\")}}: {{ doc.location }}
  • \n {% set start = frappe.utils.get_datetime(doc.start_time) %}\n {% set end = frappe.utils.get_datetime(doc.end_time) %}\n {% if start.date() == end.date() %}\n
  • {{_(\"Date\")}}: {{ start.strftime(\"%A, %d %b %Y\") }}
  • \n
  • \n {{_(\"Timing\")}}: {{ start.strftime(\"%I:%M %p\") + ' to ' + end.strftime(\"%I:%M %p\") }}\n
  • \n {% else %}\n
  • \n {{_(\"Start Time\")}}: {{ start.strftime(\"%A, %d %b %Y at %I:%M %p\") }}\n
  • \n
  • {{_(\"End Time\")}}: {{ end.strftime(\"%A, %d %b %Y at %I:%M %p\") }}
  • \n {% endif %}\n
  • {{ _(\"Event Link\") }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • \n {% if doc.is_mandatory %}\n
  • {{ _(\"Note: This Training Event is mandatory\") }}
  • \n {% endif %}\n
\n
\n
", + "modified": "2021-06-16 14:08:12.933367", "modified_by": "Administrator", "module": "HR", "name": "Training Scheduled", diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.md b/erpnext/hr/notification/training_scheduled/training_scheduled.md index 418fd4990e..b9ba846be5 100644 --- a/erpnext/hr/notification/training_scheduled/training_scheduled.md +++ b/erpnext/hr/notification/training_scheduled/training_scheduled.md @@ -24,19 +24,19 @@ {% set start = frappe.utils.get_datetime(doc.start_time) %} {% set end = frappe.utils.get_datetime(doc.end_time) %} {% if start.date() == end.date() %} -
  • {{_("Date")}}: {{ start.strftime("%A, %d %b %Y") }}
  • -
  • - {{_("Timing")}}: {{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }} -
  • +
  • {{_("Date")}}: {{ start.strftime("%A, %d %b %Y") }}
  • +
  • + {{_("Timing")}}: {{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }} +
  • {% else %} -
  • {{_("Start Time")}}: {{ start.strftime("%A, %d %b %Y at %I:%M %p") }} -
  • -
  • {{_("End Time")}}: {{ end.strftime("%A, %d %b %Y at %I:%M %p") }} -
  • +
  • + {{_("Start Time")}}: {{ start.strftime("%A, %d %b %Y at %I:%M %p") }} +
  • +
  • {{_("End Time")}}: {{ end.strftime("%A, %d %b %Y at %I:%M %p") }}
  • {% endif %} -
  • {{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • +
  • {{ _("Event Link") }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • {% if doc.is_mandatory %} -
  • Note: This Training Event is mandatory
  • +
  • {{ _("Note: This Training Event is mandatory") }}
  • {% endif %}
    @@ -44,4 +44,4 @@ - \ No newline at end of file + From 86f689e54aeafc33d2c90e5511fd04cf0458d613 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 14:23:02 +0530 Subject: [PATCH 150/180] fix(General Ledger): Get account_currency accurately --- .../report/general_ledger/general_ledger.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index aed9c4a049..8be6aaf564 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -91,7 +91,19 @@ def set_account_currency(filters): account_currency = None if filters.get("account"): - account_currency = get_account_currency(filters.account[0]) + if len(filters.get("account")) == 1: + account_currency = get_account_currency(filters.account[0]) + else: + currency = get_account_currency(filters.account[0]) + is_same_account_currency = True + for account in filters.get("account"): + if get_account_currency(account) != currency: + is_same_account_currency = False + break + + if is_same_account_currency: + account_currency = currency + elif filters.get("party"): gle_currency = frappe.db.get_value( "GL Entry", { From 60ce00531d0028741044bf7ed92b3feded139126 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 16 Jun 2021 14:25:55 +0530 Subject: [PATCH 151/180] fix: label for enabling ledger posting of change amount (#26070) --- .../doctype/accounts_settings/accounts_settings.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 2735b1ccee..0ff7230e55 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -257,9 +257,10 @@ }, { "default": "1", + "description": "If enabled, ledger entries will be posted for change amount in POS transactions", "fieldname": "post_change_gl_entries", "fieldtype": "Check", - "label": "Post Ledger Entries for Given Change" + "label": "Change Ledger Entries for Change Amount" } ], "icon": "icon-cog", @@ -267,7 +268,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-05-25 12:34:05.858669", + "modified": "2021-06-16 13:14:45.739107", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", From 8c73f6f19e90e1be54d17a3e1f88e7ed535bef90 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 16 Jun 2021 14:28:26 +0530 Subject: [PATCH 152/180] fix(pos): pos loyalty card alignment (#26051) --- erpnext/public/scss/point-of-sale.scss | 10 +++++++++- erpnext/selling/page/point_of_sale/pos_payment.js | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 9bdaa8d1ee..c77b2ce3df 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -806,6 +806,9 @@ display: none; float: right; font-weight: 700; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } > .cash-shortcuts { @@ -829,6 +832,11 @@ } } } + + > .loyalty-card { + display: flex; + flex-direction: column; + } } } @@ -1134,4 +1142,4 @@ } } } -} \ No newline at end of file +} diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 156fb777fe..c484873d3e 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -481,7 +481,7 @@ erpnext.PointOfSale.Payment = class { const amount = doc.loyalty_amount > 0 ? format_currency(doc.loyalty_amount, doc.currency) : ''; this.$payment_modes.append( `
    -
    +
    Redeem Loyalty Points
    ${amount}
    ${loyalty_program}
    @@ -563,4 +563,4 @@ erpnext.PointOfSale.Payment = class { toggle_component(show) { show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none'); } -}; \ No newline at end of file +}; From 3b1b4684ba37b950657fe32d44001738c4438df9 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 16 Jun 2021 14:30:45 +0530 Subject: [PATCH 153/180] fix: check for duplicate payment terms in Payment Term Template (#26003) --- .../doctype/payment_terms_template/payment_terms_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py index 80e3348d81..39627eb376 100644 --- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py +++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py @@ -26,7 +26,7 @@ class PaymentTermsTemplate(Document): def check_duplicate_terms(self): terms = [] for term in self.terms: - term_info = (term.credit_days, term.credit_months, term.due_date_based_on) + term_info = (term.payment_term, term.credit_days, term.credit_months, term.due_date_based_on) if term_info in terms: frappe.msgprint( _('The Payment Term at row {0} is possibly a duplicate.').format(term.idx), From 41b7c1aec0ae507430a6aa433c0c52b91a1ff92e Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 14:40:14 +0530 Subject: [PATCH 154/180] fix(General Ledger): Improve account filter --- .../report/general_ledger/general_ledger.py | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 8be6aaf564..03808c3640 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -223,17 +223,8 @@ def get_conditions(filters): conditions = [] if filters.get("account") and not filters.get("include_dimensions"): - account_conditions = "account in (select name from tabAccount where" - for account in filters["account"]: - lft, rgt = frappe.db.get_value("Account", account, ["lft", "rgt"]) - account_conditions += """ (lft>=%s and rgt<=%s) """ % (lft, rgt) - - # so that the OR doesn't get added to the last account condition - if account != filters["account"][-1]: - account_conditions += "OR" - - account_conditions += "and docstatus<2)" - conditions.append(account_conditions) + filters.account = get_accounts_with_children(filters.account) + conditions.append("account in %(account)s") if filters.get("cost_center"): filters.cost_center = get_cost_centers_with_children(filters.cost_center) @@ -291,6 +282,20 @@ def get_conditions(filters): return "and {}".format(" and ".join(conditions)) if conditions else "" +def get_accounts_with_children(accounts): + if not isinstance(accounts, list): + accounts = [d.strip() for d in accounts.strip().split(',') if d] + + all_accounts = [] + for d in accounts: + if frappe.db.exists("Account", d): + lft, rgt = frappe.db.get_value("Account", d, ["lft", "rgt"]) + children = frappe.get_all("Account", filters={"lft": [">=", lft], "rgt": ["<=", rgt]}) + all_accounts += [c.name for c in children] + else: + frappe.throw(_("Account: {0} does not exist").format(d)) + + return list(set(all_accounts)) def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries): data = [] From 6f9de8c86fbabd0498609ed3d645c89c902c8ba3 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Wed, 16 Jun 2021 20:01:29 +0530 Subject: [PATCH 155/180] fix: removed extra space from label rate --- .../doctype/purchase_invoice_item/purchase_invoice_item.json | 4 ++-- 1 file changed, 2 insertions(+), 2 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 10e1c73ea9..8a55ff87e3 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -272,7 +272,7 @@ "fieldname": "rate", "fieldtype": "Currency", "in_list_view": 1, - "label": "Rate ", + "label": "Rate", "oldfieldname": "import_rate", "oldfieldtype": "Currency", "options": "currency", @@ -854,7 +854,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2021-03-30 09:02:39.256602", + "modified": "2021-06-16 19:57:03.101571", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", From f9390f596d41004ed7645b967f29fdd9df08e412 Mon Sep 17 00:00:00 2001 From: Alan <2.alan.tom@gmail.com> Date: Thu, 17 Jun 2021 18:13:23 +0530 Subject: [PATCH 156/180] fix: auto unlink warehouse from item on delete (#26073) * fix: auto unlink warehouse from item on delete * fix: sider * refactor: use delete_doc * test: add test for unlinking warehouse from item * refactor: add msgprint to inform user of unlink * refactor: cleanup and reuse extant functions * fix: don't delete row, update table --- .../stock/doctype/warehouse/test_warehouse.py | 34 +++++++++++++++++++ erpnext/stock/doctype/warehouse/warehouse.py | 7 ++++ 2 files changed, 41 insertions(+) diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.py b/erpnext/stock/doctype/warehouse/test_warehouse.py index 95478f61f0..e3981c913e 100644 --- a/erpnext/stock/doctype/warehouse/test_warehouse.py +++ b/erpnext/stock/doctype/warehouse/test_warehouse.py @@ -11,6 +11,7 @@ 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.accounts.doctype.account.test_account import get_inventory_account, create_account +from erpnext.stock.doctype.item.test_item import create_item test_records = frappe.get_test_records('Warehouse') @@ -92,6 +93,39 @@ class TestWarehouse(unittest.TestCase): self.assertTrue(frappe.db.get_value("Warehouse", filters={"account": "Test Warehouse for Merging 2 - TCP1"})) + def test_unlinking_warehouse_from_item_defaults(self): + company = "_Test Company" + + warehouse_names = [f'_Test Warehouse {i} for Unlinking' for i in range(2)] + warehouse_ids = [] + for warehouse in warehouse_names: + warehouse_id = create_warehouse(warehouse, company=company) + warehouse_ids.append(warehouse_id) + + item_names = [f'_Test Item {i} for Unlinking' for i in range(2)] + for item, warehouse in zip(item_names, warehouse_ids): + create_item(item, warehouse=warehouse, company=company) + + # Delete warehouses + for warehouse in warehouse_ids: + frappe.delete_doc("Warehouse", warehouse) + + # Check Item existance + for item in item_names: + self.assertTrue( + bool(frappe.db.exists("Item", item)), + f"{item} doesn't exist" + ) + + item_doc = frappe.get_doc("Item", item) + for item_default in item_doc.item_defaults: + self.assertNotIn( + item_default.default_warehouse, + warehouse_ids, + f"{item} linked to {item_default.default_warehouse} in {warehouse_ids}." + ) + + def create_warehouse(warehouse_name, properties=None, company=None): if not company: company = "_Test Company" diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index 2062bddc7c..3abc13907c 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -54,6 +54,7 @@ class Warehouse(NestedSet): throw(_("Child warehouse exists for this warehouse. You can not delete this warehouse.")) self.update_nsm_model() + self.unlink_from_items() def check_if_sle_exists(self): return frappe.db.sql("""select name from `tabStock Ledger Entry` @@ -138,6 +139,12 @@ class Warehouse(NestedSet): self.save() return 1 + def unlink_from_items(self): + frappe.db.sql(""" + update `tabItem Default` + set default_warehouse=NULL + where default_warehouse=%s""", self.name) + @frappe.whitelist() def get_children(doctype, parent=None, company=None, is_root=False): if is_root: From ddef85ae97024376915b94d52ad347ba2b9c9c8c Mon Sep 17 00:00:00 2001 From: Eben van Deventer Date: Thu, 17 Jun 2021 15:13:30 +0200 Subject: [PATCH 157/180] fix: Correct South Africa VAT Rate (#25894) On 1 April 2018 South Africa increased the VAT rate from 14% to 15%, this proposed change seeks to update the default parameters for a fresh ERPNext installation. --- erpnext/setup/setup_wizard/data/country_wise_tax.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json index ec9a6d6b70..daaa626a81 100644 --- a/erpnext/setup/setup_wizard/data/country_wise_tax.json +++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json @@ -1867,7 +1867,7 @@ "South Africa": { "South Africa Tax": { "account_name": "VAT", - "tax_rate": 14.00 + "tax_rate": 15.00 } }, From ef972693861bb2b39071351249292fe1ab136432 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 18 Jun 2021 10:11:53 +0530 Subject: [PATCH 158/180] fix: timeout while cancelling stock reconciliation --- .../doctype/stock_reconciliation/stock_reconciliation.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 306df99b3c..2b51c1a5c3 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -473,6 +473,13 @@ class StockReconciliation(StockController): else: self._submit() + def cancel(self): + if len(self.items) > 100: + msgprint(_("The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Submitted stage")) + self.queue_action('cancel', timeout=2000) + else: + self._cancel() + @frappe.whitelist() def get_items(warehouse, posting_date, posting_time, company): lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) From b066fe9519726adc26cf7a0a065f4504d8cc6e1d Mon Sep 17 00:00:00 2001 From: Anuja Pawar <60467153+Anuja-pawar@users.noreply.github.com> Date: Fri, 18 Jun 2021 11:29:07 +0530 Subject: [PATCH 159/180] fix: insufficient permission for dunning error (#26092) --- erpnext/accounts/doctype/dunning/dunning.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py index 1a6dbedf56..c6c689212b 100644 --- a/erpnext/accounts/doctype/dunning/dunning.py +++ b/erpnext/accounts/doctype/dunning/dunning.py @@ -86,7 +86,7 @@ def resolve_dunning(doc, state): for reference in doc.references: if reference.reference_doctype == 'Sales Invoice' and reference.outstanding_amount <= 0: dunnings = frappe.get_list('Dunning', filters={ - 'sales_invoice': reference.reference_name, 'status': ('!=', 'Resolved')}) + 'sales_invoice': reference.reference_name, 'status': ('!=', 'Resolved')}, ignore_permissions=True) for dunning in dunnings: frappe.db.set_value("Dunning", dunning.name, "status", 'Resolved') @@ -96,7 +96,7 @@ def calculate_interest_and_amount(posting_date, outstanding_amount, rate_of_inte grand_total = 0 if rate_of_interest: interest_per_year = flt(outstanding_amount) * flt(rate_of_interest) / 100 - interest_amount = (interest_per_year * cint(overdue_days)) / 365 + interest_amount = (interest_per_year * cint(overdue_days)) / 365 grand_total = flt(outstanding_amount) + flt(interest_amount) + flt(dunning_fee) dunning_amount = flt(interest_amount) + flt(dunning_fee) return { From 3d8f82459b0bd90c80aec473f9a0daa5a7564db8 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Fri, 18 Jun 2021 11:42:28 +0530 Subject: [PATCH 160/180] fix(Issue): reset response_by and resolution_by if SLA is removed (#25997) --- erpnext/support/doctype/issue/issue.json | 6 +++--- erpnext/support/doctype/issue/issue.py | 12 +++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index bc29821ee2..14712f89fe 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -166,7 +166,7 @@ "options": "Service Level Agreement" }, { - "depends_on": "eval: doc.status != 'Replied';", + "depends_on": "eval: doc.status != 'Replied' && doc.service_level_agreement;", "fieldname": "response_by", "fieldtype": "Datetime", "label": "Response By", @@ -180,7 +180,7 @@ "read_only": 1 }, { - "depends_on": "eval: doc.status != 'Replied';", + "depends_on": "eval: doc.status != 'Replied' && doc.service_level_agreement;", "fieldname": "resolution_by", "fieldtype": "Datetime", "label": "Resolution By", @@ -410,7 +410,7 @@ "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2021-05-26 10:49:07.574769", + "modified": "2021-06-10 03:22:27.098898", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index b068363f06..9c69deb6a4 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -29,6 +29,9 @@ class Issue(Document): self.update_status() self.set_lead_contact(self.raised_by) + if not self.service_level_agreement: + self.reset_sla_fields() + def on_update(self): # Add a communication in the issue timeline if self.flags.create_communication and self.via_customer_portal: @@ -54,6 +57,13 @@ class Issue(Document): self.company = frappe.db.get_value("Lead", self.lead, "company") or \ frappe.db.get_default("Company") + def reset_sla_fields(self): + self.agreement_status = "" + self.response_by = "" + self.resolution_by = "" + self.response_by_variance = 0 + self.resolution_by_variance = 0 + def update_status(self): status = frappe.db.get_value("Issue", self.name, "status") if self.status != "Open" and status == "Open" and not self.first_responded_on: @@ -511,4 +521,4 @@ def get_time_in_timedelta(time): Converts datetime.time(10, 36, 55, 961454) to datetime.timedelta(seconds=38215) """ import datetime - return datetime.timedelta(hours=time.hour, minutes=time.minute, seconds=time.second) \ No newline at end of file + return datetime.timedelta(hours=time.hour, minutes=time.minute, seconds=time.second) From 81c97c13ce1b697acc16d38f1e1084f5da573882 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 18 Jun 2021 17:25:37 +0530 Subject: [PATCH 161/180] fix: Sanctioned loan amount limit check --- erpnext/loan_management/doctype/loan/loan.py | 29 +++++- .../loan_management/doctype/loan/test_loan.py | 45 ++++++++- .../loan_application/loan_application.py | 4 +- .../doctype/loan_repayment/loan_repayment.py | 91 ++++++++++--------- 4 files changed, 116 insertions(+), 53 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index 69d11a8653..ff7fbbdf49 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -60,8 +60,9 @@ class Loan(AccountsController): self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods) def check_sanctioned_amount_limit(self): - total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company) sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company) + if sanctioned_amount_limit: + total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company) if sanctioned_amount_limit and flt(self.loan_amount) + flt(total_loan_amount) > flt(sanctioned_amount_limit): frappe.throw(_("Sanctioned Amount limit crossed for {0} {1}").format(self.applicant_type, frappe.bold(self.applicant))) @@ -155,9 +156,29 @@ def update_total_amount_paid(doc): frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid) def get_total_loan_amount(applicant_type, applicant, company): - return frappe.db.get_value('Loan', - {'applicant_type': applicant_type, 'company': company, 'applicant': applicant, 'docstatus': 1}, - 'sum(loan_amount)') + pending_amount = 0 + loan_details = frappe.db.get_all("Loan", + filters={"applicant_type": applicant_type, "company": company, "applicant": applicant, "docstatus": 1, + "status": ("!=", "Closed")}, + fields=["status", "total_payment", "disbursed_amount", "total_interest_payable", "total_principal_paid", + "written_off_amount"]) + + interest_amount = flt(frappe.db.get_value("Loan Interest Accrual", {"applicant_type": applicant_type, + "company": company, "applicant": applicant, "docstatus": 1}, "sum(interest_amount - paid_interest_amount)")) + + for loan in loan_details: + if loan.status in ("Disbursed", "Loan Closure Requested"): + pending_amount += flt(loan.total_payment) - flt(loan.total_interest_payable) \ + - flt(loan.total_principal_paid) - flt(loan.written_off_amount) + elif loan.status == "Partially Disbursed": + pending_amount += flt(loan.disbursed_amount) - flt(loan.total_interest_payable) \ + - flt(loan.total_principal_paid) - flt(loan.written_off_amount) + elif loan.status == "Sanctioned": + pending_amount += flt(loan.total_payment) + + pending_amount += interest_amount + + return pending_amount def get_sanctioned_amount_limit(applicant_type, applicant, company): return frappe.db.get_value('Sanctioned Loan Amount', diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index fa4707ce2b..314f58dd15 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -49,7 +49,11 @@ class TestLoan(unittest.TestCase): if not frappe.db.exists("Customer", "_Test Loan Customer"): frappe.get_doc(get_customer_dict('_Test Loan Customer')).insert(ignore_permissions=True) - self.applicant2 = frappe.db.get_value("Customer", {'name': '_Test Loan Customer'}, 'name') + if not frappe.db.exists("Customer", "_Test Loan Customer 1"): + frappe.get_doc(get_customer_dict("_Test Loan Customer 1")).insert(ignore_permissions=True) + + self.applicant2 = frappe.db.get_value("Customer", {"name": "_Test Loan Customer"}, "name") + self.applicant3 = frappe.db.get_value("Customer", {"name": "_Test Loan Customer 1"}, "name") create_loan(self.applicant1, "Personal Loan", 280000, "Repay Over Number of Periods", 20) @@ -125,6 +129,38 @@ class TestLoan(unittest.TestCase): self.assertTrue(gl_entries1) self.assertTrue(gl_entries2) + def test_sanctioned_amount_limit(self): + # Clear loan docs before checking + frappe.db.sql("DELETE FROM `tabLoan` where applicant = '_Test Loan Customer 1'") + frappe.db.sql("DELETE FROM `tabLoan Application` where applicant = '_Test Loan Customer 1'") + frappe.db.sql("DELETE FROM `tabLoan Security Pledge` where applicant = '_Test Loan Customer 1'") + + if not frappe.db.get_value("Sanctioned Loan Amount", filters={"applicant_type": "Customer", + "applicant": "_Test Loan Customer 1", "company": "_Test Company"}): + frappe.get_doc({ + "doctype": "Sanctioned Loan Amount", + "applicant_type": "Customer", + "applicant": "_Test Loan Customer 1", + "sanctioned_amount_limit": 1500000, + "company": "_Test Company" + }).insert(ignore_permissions=True) + + # Make First Loan + pledge = [{ + "loan_security": "Test Security 1", + "qty": 4000.00 + }] + + loan_application = create_loan_application('_Test Company', self.applicant3, 'Demand Loan', pledge) + create_pledge(loan_application) + loan = create_demand_loan(self.applicant3, "Demand Loan", loan_application, posting_date='2019-10-01') + loan.submit() + + # Make second loan greater than the sanctioned amount + loan_application = create_loan_application('_Test Company', self.applicant3, 'Demand Loan', pledge, + do_not_save=True) + self.assertRaises(frappe.ValidationError, loan_application.save) + def test_regular_loan_repayment(self): pledge = [{ "loan_security": "Test Security 1", @@ -367,7 +403,7 @@ class TestLoan(unittest.TestCase): unpledge_request.load_from_db() self.assertEqual(unpledge_request.docstatus, 1) - def test_santined_loan_security_unpledge(self): + def test_sanctioned_loan_security_unpledge(self): pledge = [{ "loan_security": "Test Security 1", "qty": 4000.00 @@ -858,7 +894,7 @@ def create_repayment_entry(loan, applicant, posting_date, paid_amount): return lr def create_loan_application(company, applicant, loan_type, proposed_pledges, repayment_method=None, - repayment_periods=None, posting_date=None): + repayment_periods=None, posting_date=None, do_not_save=False): loan_application = frappe.new_doc('Loan Application') loan_application.applicant_type = 'Customer' loan_application.company = company @@ -874,6 +910,9 @@ def create_loan_application(company, applicant, loan_type, proposed_pledges, rep for pledge in proposed_pledges: loan_application.append('proposed_pledges', pledge) + if do_not_save: + return loan_application + loan_application.save() loan_application.submit() diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py index 9c0147e55b..d8f3577b2c 100644 --- a/erpnext/loan_management/doctype/loan_application/loan_application.py +++ b/erpnext/loan_management/doctype/loan_application/loan_application.py @@ -46,9 +46,11 @@ class LoanApplication(Document): frappe.throw(_("Loan Amount exceeds maximum loan amount of {0} as per proposed securities").format(self.maximum_loan_amount)) def check_sanctioned_amount_limit(self): - total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company) sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company) + if sanctioned_amount_limit: + total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company) + if sanctioned_amount_limit and flt(self.loan_amount) + flt(total_loan_amount) > flt(sanctioned_amount_limit): frappe.throw(_("Sanctioned Amount limit crossed for {0} {1}").format(self.applicant_type, frappe.bold(self.applicant))) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 3d99b1f304..b8b1a40b5f 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -235,70 +235,71 @@ class LoanRepayment(AccountsController): else: remarks = _("Repayment against Loan: ") + self.against_loan - if self.total_penalty_paid: + if not loan_details.repay_from_salary: + if self.total_penalty_paid: + gle_map.append( + self.get_gl_dict({ + "account": loan_details.loan_account, + "against": loan_details.payment_account, + "debit": self.total_penalty_paid, + "debit_in_account_currency": self.total_penalty_paid, + "against_voucher_type": "Loan", + "against_voucher": self.against_loan, + "remarks": _("Penalty against loan:") + self.against_loan, + "cost_center": self.cost_center, + "party_type": self.applicant_type, + "party": self.applicant, + "posting_date": getdate(self.posting_date) + }) + ) + + gle_map.append( + self.get_gl_dict({ + "account": loan_details.penalty_income_account, + "against": loan_details.payment_account, + "credit": self.total_penalty_paid, + "credit_in_account_currency": self.total_penalty_paid, + "against_voucher_type": "Loan", + "against_voucher": self.against_loan, + "remarks": _("Penalty against loan:") + self.against_loan, + "cost_center": self.cost_center, + "posting_date": getdate(self.posting_date) + }) + ) + gle_map.append( self.get_gl_dict({ - "account": loan_details.loan_account, - "against": loan_details.payment_account, - "debit": self.total_penalty_paid, - "debit_in_account_currency": self.total_penalty_paid, + "account": loan_details.payment_account, + "against": loan_details.loan_account + ", " + loan_details.interest_income_account + + ", " + loan_details.penalty_income_account, + "debit": self.amount_paid, + "debit_in_account_currency": self.amount_paid, "against_voucher_type": "Loan", "against_voucher": self.against_loan, - "remarks": _("Penalty against loan:") + self.against_loan, + "remarks": remarks, "cost_center": self.cost_center, - "party_type": self.applicant_type, - "party": self.applicant, "posting_date": getdate(self.posting_date) }) ) gle_map.append( self.get_gl_dict({ - "account": loan_details.penalty_income_account, + "account": loan_details.loan_account, + "party_type": loan_details.applicant_type, + "party": loan_details.applicant, "against": loan_details.payment_account, - "credit": self.total_penalty_paid, - "credit_in_account_currency": self.total_penalty_paid, + "credit": self.amount_paid, + "credit_in_account_currency": self.amount_paid, "against_voucher_type": "Loan", "against_voucher": self.against_loan, - "remarks": _("Penalty against loan:") + self.against_loan, + "remarks": remarks, "cost_center": self.cost_center, "posting_date": getdate(self.posting_date) }) ) - gle_map.append( - self.get_gl_dict({ - "account": loan_details.payment_account, - "against": loan_details.loan_account + ", " + loan_details.interest_income_account - + ", " + loan_details.penalty_income_account, - "debit": self.amount_paid, - "debit_in_account_currency": self.amount_paid, - "against_voucher_type": "Loan", - "against_voucher": self.against_loan, - "remarks": remarks, - "cost_center": self.cost_center, - "posting_date": getdate(self.posting_date) - }) - ) - - gle_map.append( - self.get_gl_dict({ - "account": loan_details.loan_account, - "party_type": loan_details.applicant_type, - "party": loan_details.applicant, - "against": loan_details.payment_account, - "credit": self.amount_paid, - "credit_in_account_currency": self.amount_paid, - "against_voucher_type": "Loan", - "against_voucher": self.against_loan, - "remarks": remarks, - "cost_center": self.cost_center, - "posting_date": getdate(self.posting_date) - }) - ) - - if gle_map: - make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False) + if gle_map: + make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False) def create_repayment_entry(loan, applicant, company, posting_date, loan_type, payment_type, interest_payable, payable_principal_amount, amount_paid, penalty_amount=None): From 8520edc952d252aa4ebdbf9bf56e2b04a12dd614 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 15 Jun 2021 10:21:44 +0530 Subject: [PATCH 162/180] fix: time out while submitting the stock transactions with more than 50 items --- erpnext/controllers/buying_controller.py | 10 ++- erpnext/controllers/stock_controller.py | 79 ++++++++++++++----- .../stock_ledger_entry/stock_ledger_entry.py | 19 ++--- erpnext/stock/stock_ledger.py | 16 +++- erpnext/stock/utils.py | 2 +- 5 files changed, 93 insertions(+), 33 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index da819119b1..20f5445725 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -171,12 +171,13 @@ class BuyingController(StockController): TODO: rename item_tax_amount to valuation_tax_amount """ + stock_and_asset_items = [] stock_and_asset_items = self.get_stock_items() + self.get_asset_items() stock_and_asset_items_qty, stock_and_asset_items_amount = 0, 0 last_item_idx = 1 for d in self.get("items"): - if d.item_code and d.item_code in stock_and_asset_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) last_item_idx = d.idx @@ -683,7 +684,8 @@ class BuyingController(StockController): self.process_fixed_asset() self.update_fixed_asset(field) - update_last_purchase_rate(self, is_submit = 1) + if self.doctype in ['Purchase Order', 'Purchase Receipt']: + update_last_purchase_rate(self, is_submit = 1) def on_cancel(self): super(BuyingController, self).on_cancel() @@ -691,7 +693,9 @@ class BuyingController(StockController): if self.get('is_return'): return - update_last_purchase_rate(self, is_submit = 0) + if self.doctype in ['Purchase Order', 'Purchase Receipt']: + update_last_purchase_rate(self, is_submit = 0) + if self.doctype in ['Purchase Receipt', 'Purchase Invoice']: field = 'purchase_invoice' if self.doctype == 'Purchase Invoice' else 'purchase_receipt' diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 6a7c9e3d0e..35097b97b9 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -501,7 +501,6 @@ class StockController(AccountsController): check_if_stock_and_account_balance_synced(self.posting_date, self.company, self.doctype, self.name) - @frappe.whitelist() def make_quality_inspections(doctype, docname, items): if isinstance(items, str): @@ -533,21 +532,75 @@ def make_quality_inspections(doctype, docname, items): return inspections - def is_reposting_pending(): return frappe.db.exists("Repost Item Valuation", {'docstatus': 1, 'status': ['in', ['Queued','In Progress']]}) +def future_sle_exists(args, sl_entries=None): + key = (args.voucher_type, args.voucher_no) -def future_sle_exists(args): - sl_entries = frappe.get_all("Stock Ledger Entry", + if validate_future_sle_not_exists(args, key, sl_entries): + return False + elif get_cached_data(args, key): + return True + + if not sl_entries: + sl_entries = get_sle_entries_against_voucher(args) + if not sl_entries: + return + + or_conditions = get_conditions_to_validate_future_sle(sl_entries) + + data = frappe.db.sql(""" + select item_code, warehouse, count(name) as total_row + from `tabStock Ledger Entry` + where + ({}) + and timestamp(posting_date, posting_time) + >= timestamp(%(posting_date)s, %(posting_time)s) + and voucher_no != %(voucher_no)s + and is_cancelled = 0 + GROUP BY + item_code, warehouse + """.format(" or ".join(or_conditions)), args, as_dict=1) + + for d in data: + frappe.local.future_sle[key][(d.item_code, d.warehouse)] = d.total_row + + return len(data) + +def validate_future_sle_not_exists(args, key, sl_entries=None): + item_key = '' + if args.get('item_code'): + item_key = (args.get('item_code'), args.get('warehouse')) + + if not sl_entries and hasattr(frappe.local, 'future_sle'): + if (not frappe.local.future_sle.get(key) or + (item_key and item_key not in frappe.local.future_sle.get(key))): + return True + +def get_cached_data(args, key): + if not hasattr(frappe.local, 'future_sle'): + frappe.local.future_sle = {} + + if key not in frappe.local.future_sle: + frappe.local.future_sle[key] = frappe._dict({}) + + if args.get('item_code'): + item_key = (args.get('item_code'), args.get('warehouse')) + count = frappe.local.future_sle[key].get(item_key) + + return True if (count or count == 0) else False + else: + return frappe.local.future_sle[key] + +def get_sle_entries_against_voucher(args): + return frappe.get_all("Stock Ledger Entry", filters={"voucher_type": args.voucher_type, "voucher_no": args.voucher_no}, fields=["item_code", "warehouse"], order_by="creation asc") - if not sl_entries: - return - +def get_conditions_to_validate_future_sle(sl_entries): warehouse_items_map = {} for entry in sl_entries: if entry.warehouse not in warehouse_items_map: @@ -561,17 +614,7 @@ def future_sle_exists(args): f"""warehouse = {frappe.db.escape(warehouse)} and item_code in ({', '.join(frappe.db.escape(item) for item in items)})""") - return frappe.db.sql(""" - select name - from `tabStock Ledger Entry` - where - ({}) - 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 - """.format(" or ".join(or_conditions)), args) + return or_conditions def create_repost_item_valuation_entry(args): args = frappe._dict(args) 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 b0e7440e6c..0febcb6891 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import flt, getdate, add_days, formatdate, get_datetime, date_diff +from frappe.utils import flt, getdate, add_days, formatdate, get_datetime, cint from frappe.model.document import Document from datetime import date from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock @@ -108,17 +108,18 @@ class StockLedgerEntry(Document): self.stock_uom = item_det.stock_uom def check_stock_frozen_date(self): - stock_frozen_upto = frappe.db.get_value('Stock Settings', None, 'stock_frozen_upto') or '' - if stock_frozen_upto: - stock_auth_role = frappe.db.get_value('Stock Settings', None,'stock_auth_role') - if getdate(self.posting_date) <= getdate(stock_frozen_upto) and not stock_auth_role in frappe.get_roles(): - frappe.throw(_("Stock transactions before {0} are frozen").format(formatdate(stock_frozen_upto)), StockFreezeError) + stock_settings = frappe.get_doc('Stock Settings', 'Stock Settings') - stock_frozen_upto_days = int(frappe.db.get_value('Stock Settings', None, 'stock_frozen_upto_days') or 0) + if stock_settings.stock_frozen_upto: + if (getdate(self.posting_date) <= getdate(stock_settings.stock_frozen_upto) + and stock_settings.stock_auth_role not in frappe.get_roles()): + frappe.throw(_("Stock transactions before {0} are frozen") + .format(formatdate(stock_settings.stock_frozen_upto)), StockFreezeError) + + stock_frozen_upto_days = cint(stock_settings.stock_frozen_upto_days) if stock_frozen_upto_days: - stock_auth_role = frappe.db.get_value('Stock Settings', None,'stock_auth_role') older_than_x_days_ago = (add_days(getdate(self.posting_date), stock_frozen_upto_days) <= date.today()) - if older_than_x_days_ago and not stock_auth_role in frappe.get_roles(): + if older_than_x_days_ago and stock_settings.stock_auth_role not in frappe.get_roles(): frappe.throw(_("Not allowed to update stock transactions older than {0}").format(stock_frozen_upto_days), StockFreezeError) def scrub_posting_time(self): diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index fc82c789cc..fb2ecab249 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -22,6 +22,7 @@ _exceptions = frappe.local('stockledger_exceptions') # _exceptions = [] def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False): + from erpnext.controllers.stock_controller import future_sle_exists if sl_entries: from erpnext.stock.utils import update_bin @@ -30,6 +31,9 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc validate_cancellation(sl_entries) set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no')) + args = get_args_for_future_sle(sl_entries[0]) + future_sle_exists(args, sl_entries) + for sle in sl_entries: if sle.serial_no: validate_serial_no(sle) @@ -53,6 +57,14 @@ 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 get_args_for_future_sle(row): + return frappe._dict({ + 'voucher_type': row.get('voucher_type'), + 'voucher_no': row.get('voucher_no'), + 'posting_date': row.get('posting_date'), + 'posting_time': row.get('posting_time') + }) + def validate_serial_no(sle): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos for sn in get_serial_nos(sle.serial_no): @@ -472,8 +484,8 @@ class update_entries_after(object): 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_doc(sle.voucher_type, sle.voucher_no) + if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_subcontracted") == 'Yes': + 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() diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 034d3ebbb5..8a6a3a3e4a 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -177,7 +177,7 @@ def get_bin(item_code, warehouse): return bin_obj def update_bin(args, allow_negative_stock=False, via_landed_cost_voucher=False): - is_stock_item = frappe.db.get_value('Item', args.get("item_code"), 'is_stock_item') + is_stock_item = frappe.get_cached_value('Item', args.get("item_code"), 'is_stock_item') if is_stock_item: bin = get_bin(args.get("item_code"), args.get("warehouse")) bin.update_stock(args, allow_negative_stock, via_landed_cost_voucher) From 8c844e4515afc5f9f241c522578b2e81d19f24b9 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 11 Jun 2021 17:27:08 +0530 Subject: [PATCH 163/180] fix: material request and supplier quotation not linked if sq created from supplier portal against rfq --- .../request_for_quotation.py | 22 ++++++++++--------- .../templates/includes/transaction_row.html | 8 ++++--- 2 files changed, 17 insertions(+), 13 deletions(-) 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 0127eb8163..a4ce84e1cf 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -317,19 +317,21 @@ def add_items(sq_doc, supplier, items): create_rfq_items(sq_doc, supplier, data) def create_rfq_items(sq_doc, supplier, data): - sq_doc.append('items', { - "item_code": data.item_code, - "item_name": data.item_name, - "description": data.description, - "qty": data.qty, - "rate": data.rate, - "conversion_factor": data.conversion_factor if data.conversion_factor else None, - "supplier_part_no": frappe.db.get_value("Item Supplier", {'parent': data.item_code, 'supplier': supplier}, "supplier_part_no"), - "warehouse": data.warehouse or '', + args = {} + + for field in ['item_code', 'item_name', 'description', 'qty', 'rate', 'conversion_factor', + 'warehouse', 'material_request', 'material_request_item', 'stock_qty']: + args[field] = data.get(field) + + args.update({ "request_for_quotation_item": data.name, - "request_for_quotation": data.parent + "request_for_quotation": data.parent, + "supplier_part_no": frappe.db.get_value("Item Supplier", + {'parent': data.item_code, 'supplier': supplier}, "supplier_part_no") }) + sq_doc.append('items', args) + @frappe.whitelist() def get_pdf(doctype, name, supplier): doc = get_rfq_doc(doctype, name, supplier) diff --git a/erpnext/templates/includes/transaction_row.html b/erpnext/templates/includes/transaction_row.html index 383413103e..3cfb8d8440 100644 --- a/erpnext/templates/includes/transaction_row.html +++ b/erpnext/templates/includes/transaction_row.html @@ -13,9 +13,11 @@ {{ doc.items_preview }}
    -
    - {{ doc.get_formatted("grand_total") }} -
    + {% if doc.get('grand_total') %} +
    + {{ doc.get_formatted("grand_total") }} +
    + {% endif %}
    Link From a94b89727c550cc1eaea86379a2cb4d53297a8b8 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 24 May 2021 20:11:15 +0530 Subject: [PATCH 164/180] feat: subcontract code refactor and enhancement --- .../purchase_invoice/test_purchase_invoice.py | 2 + .../doctype/purchase_order/purchase_order.js | 38 +- .../purchase_order/purchase_order.json | 3 +- .../doctype/purchase_order/purchase_order.py | 59 +- .../purchase_order/test_purchase_order.py | 100 +-- .../purchase_order_item_supplied.json | 45 +- .../purchase_receipt_item_supplied.json | 17 +- .../subcontract_order_summary/__init__.py | 0 .../subcontract_order_summary.js | 45 ++ .../subcontract_order_summary.json | 32 + .../subcontract_order_summary.py | 158 +++++ erpnext/controllers/buying_controller.py | 427 +------------ erpnext/controllers/subcontracting.py | 342 ++++++++++ erpnext/manufacturing/doctype/bom/test_bom.py | 2 + erpnext/stock/doctype/bin/bin.py | 4 +- .../item_alternative/test_item_alternative.py | 5 + .../purchase_receipt/purchase_receipt.js | 2 + .../purchase_receipt/purchase_receipt.json | 5 +- .../purchase_receipt/purchase_receipt.py | 2 + .../purchase_receipt/test_purchase_receipt.py | 4 + .../stock/doctype/stock_entry/stock_entry.js | 4 + .../doctype/stock_entry/stock_entry.json | 15 +- .../stock/doctype/stock_entry/stock_entry.py | 79 ++- erpnext/tests/test_subcontracting.py | 583 ++++++++++++++++++ 24 files changed, 1418 insertions(+), 555 deletions(-) create mode 100644 erpnext/buying/report/subcontract_order_summary/__init__.py create mode 100644 erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js create mode 100644 erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.json create mode 100644 erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py create mode 100644 erpnext/controllers/subcontracting.py create mode 100644 erpnext/tests/test_subcontracting.py diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 503dda7728..ff433b962f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -621,8 +621,10 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(actual_qty_0, get_qty_after_transaction()) def test_subcontracting_via_purchase_invoice(self): + from erpnext.buying.doctype.purchase_order.test_purchase_order import update_backflush_based_on from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + update_backflush_based_on('BOM') make_stock_entry(item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100) make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 0f6d927b36..440cde6d9e 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -53,6 +53,38 @@ frappe.ui.form.on("Purchase Order", { } else { frm.set_value("tax_withholding_category", frm.supplier_tds); } + }, + + refresh: function(frm) { + frm.trigger('get_materials_from_supplier'); + }, + + get_materials_from_supplier: function(frm) { + let po_details = []; + + if (frm.doc.supplied_items && (frm.doc.per_received == 100 || frm.doc.status === 'Closed')) { + frm.doc.supplied_items.forEach(d => { + if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) { + po_details.push(d.name) + } + }); + } + + if (po_details && po_details.length) { + frm.add_custom_button(__('Return of Components'), () => { + frm.call({ + method: 'erpnext.buying.doctype.purchase_order.purchase_order.get_materials_from_supplier', + freeze_message: __('Creating Stock Entry'), + args: { purchase_order: frm.doc.name, po_details: po_details }, + callback: function(r) { + if (r && r.message) { + const doc = frappe.model.sync(r.message); + frappe.set_route("Form", doc[0].doctype, doc[0].name); + } + } + }); + }, __('Create')); + } } }); @@ -217,7 +249,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( }, has_unsupplied_items: function() { - return this.frm.doc['supplied_items'].some(item => item.required_qty != item.supplied_qty) + return this.frm.doc['supplied_items'].some(item => item.required_qty > item.supplied_qty) }, make_stock_entry: function() { @@ -513,12 +545,14 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( ], primary_action: function() { var data = d.get_values(); + var content_msg = 'Reason for hold: ' + data.reason_for_hold; + frappe.call({ method: "frappe.desk.form.utils.add_comment", args: { reference_doctype: me.frm.doctype, reference_name: me.frm.docname, - content: __('Reason for hold:') + " " +data.reason_for_hold, + content: __(content_msg), comment_email: frappe.session.user, comment_by: frappe.session.user_fullname }, diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 41668c6291..bb0ad60cab 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -609,6 +609,7 @@ "fieldname": "supplied_items", "fieldtype": "Table", "label": "Supplied Items", + "no_copy": 1, "oldfieldname": "po_raw_material_details", "oldfieldtype": "Table", "options": "Purchase Order Item Supplied", @@ -1377,7 +1378,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2021-04-19 00:55:30.781375", + "modified": "2021-05-30 15:17:53.663648", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 2629ba7d61..724f863e0f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -503,9 +503,10 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions @frappe.whitelist() def make_rm_stock_entry(purchase_order, rm_items): + rm_items_list = rm_items if isinstance(rm_items, string_types): rm_items_list = json.loads(rm_items) - else: + elif not rm_items: frappe.throw(_("No Items available for transfer")) if rm_items_list: @@ -543,6 +544,8 @@ def make_rm_stock_entry(purchase_order, rm_items): 'qty': rm_item_data["qty"], 'from_warehouse': rm_item_data["warehouse"], 'stock_uom': rm_item_data["stock_uom"], + 'serial_no': rm_item_data.get('serial_no'), + 'batch_no': rm_item_data.get('batch_no'), 'main_item_code': rm_item_data["item_code"], 'allow_alternative_item': item_wh.get(rm_item_code, {}).get('allow_alternative_item') } @@ -582,3 +585,57 @@ def update_status(status, name): def make_inter_company_sales_order(source_name, target_doc=None): from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction return make_inter_company_transaction("Purchase Order", source_name, target_doc) + +@frappe.whitelist() +def get_materials_from_supplier(purchase_order, po_details): + if isinstance(po_details, string_types): + po_details = json.loads(po_details) + + doc = frappe.get_cached_doc('Purchase Order', purchase_order) + doc.initialized_fields() + doc.purchase_orders = [doc.name] + doc.get_available_materials() + + if not doc.available_materials: + frappe.throw(_('Materials are already received against the purchase order {0}') + .format(purchase_order)) + + return make_return_stock_entry_for_subcontract(doc.available_materials, doc, po_details) + +def make_return_stock_entry_for_subcontract(available_materials, po_doc, po_details): + ste_doc = frappe.new_doc('Stock Entry') + ste_doc.purpose = 'Material Transfer' + ste_doc.purchase_order = po_doc.name + ste_doc.company = po_doc.company + ste_doc.is_return = 1 + + for key, value in available_materials.items(): + if not value.qty: + continue + + if value.batch_no: + for batch_no, qty in value.batch_no.items(): + add_items_in_ste(ste_doc, value, value.qty, po_details, batch_no) + else: + add_items_in_ste(ste_doc, value, value.qty, po_details) + + ste_doc.set_stock_entry_type() + ste_doc.calculate_rate_and_amount() + + return ste_doc + +def add_items_in_ste(ste_doc, row, qty, po_details, batch_no=None): + item = ste_doc.append('items', row.item_details) + + po_detail = list(set(row.po_details).intersection(po_details)) + item.update({ + 'qty': qty, + 'batch_no': batch_no, + 'basic_rate': row.item_details['rate'], + 'po_detail': po_detail[0] if po_detail else '', + 's_warehouse': row.item_details['t_warehouse'], + 't_warehouse': row.item_details['s_warehouse'], + 'item_code': row.item_details['rm_item_code'], + 'subcontracted_item': row.item_details['main_item_code'], + 'serial_no': '\n'.join(row.serial_no) if row.serial_no else '' + }) \ No newline at end of file diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 3b9f8e9775..33d1971451 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -20,7 +20,6 @@ from erpnext.controllers.status_updater import OverAllowanceError from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order from erpnext.stock.doctype.batch.test_batch import make_new_batch -from erpnext.controllers.buying_controller import get_backflushed_subcontracted_raw_materials class TestPurchaseOrder(unittest.TestCase): def test_make_purchase_receipt(self): @@ -771,7 +770,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(bin11.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) def test_exploded_items_in_subcontracted(self): - item_code = "_Test Subcontracted FG Item 1" + item_code = "_Test Subcontracted FG Item 11" make_subcontracted_item(item_code=item_code) po = create_purchase_order(item_code=item_code, qty=1, @@ -853,76 +852,6 @@ class TestPurchaseOrder(unittest.TestCase): update_backflush_based_on("BOM") - def test_backflushed_based_on_for_multiple_batches(self): - item_code = "_Test Subcontracted FG Item 2" - make_item('Sub Contracted Raw Material 2', { - 'is_stock_item': 1, - 'is_sub_contracted_item': 1 - }) - - make_subcontracted_item(item_code=item_code, has_batch_no=1, create_new_batch=1, - raw_materials=["Sub Contracted Raw Material 2"]) - - update_backflush_based_on("Material Transferred for Subcontract") - - order_qty = 500 - po = create_purchase_order(item_code=item_code, qty=order_qty, - is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC") - - make_stock_entry(target="_Test Warehouse - _TC", - item_code = "Sub Contracted Raw Material 2", qty=552, basic_rate=100) - - rm_items = [ - {"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 2","item_name":"_Test Item", - "qty":552,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"}] - - rm_item_string = json.dumps(rm_items) - se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string)) - se.submit() - - for batch in ["ABCD1", "ABCD2", "ABCD3", "ABCD4"]: - make_new_batch(batch_id=batch, item_code=item_code) - - pr = make_purchase_receipt(po.name) - - # partial receipt - pr.get('items')[0].qty = 30 - pr.get('items')[0].batch_no = "ABCD1" - - purchase_order = po.name - purchase_order_item = po.items[0].name - - for batch_no, qty in {"ABCD2": 60, "ABCD3": 70, "ABCD4":40}.items(): - pr.append("items", { - "item_code": pr.get('items')[0].item_code, - "item_name": pr.get('items')[0].item_name, - "uom": pr.get('items')[0].uom, - "stock_uom": pr.get('items')[0].stock_uom, - "warehouse": pr.get('items')[0].warehouse, - "conversion_factor": pr.get('items')[0].conversion_factor, - "cost_center": pr.get('items')[0].cost_center, - "rate": pr.get('items')[0].rate, - "qty": qty, - "batch_no": batch_no, - "purchase_order": purchase_order, - "purchase_order_item": purchase_order_item - }) - - pr.submit() - - pr1 = make_purchase_receipt(po.name) - pr1.get('items')[0].qty = 300 - pr1.get('items')[0].batch_no = "ABCD1" - pr1.save() - - pr_key = ("Sub Contracted Raw Material 2", po.name) - consumed_qty = get_backflushed_subcontracted_raw_materials([po.name]).get(pr_key) - - self.assertTrue(pr1.supplied_items[0].consumed_qty > 0) - self.assertTrue(pr1.supplied_items[0].consumed_qty, flt(552.0) - flt(consumed_qty)) - - update_backflush_based_on("BOM") - def test_supplied_qty_against_subcontracted_po(self): item_code = "_Test Subcontracted FG Item 5" make_item('Sub Contracted Raw Material 4', { @@ -1117,22 +1046,29 @@ def create_purchase_order(**args): po.conversion_factor = args.conversion_factor or 1 po.supplier_warehouse = args.supplier_warehouse or None - po.append("items", { - "item_code": args.item or args.item_code or "_Test Item", - "warehouse": args.warehouse or "_Test Warehouse - _TC", - "qty": args.qty or 10, - "rate": args.rate or 500, - "schedule_date": add_days(nowdate(), 1), - "include_exploded_items": args.get('include_exploded_items', 1), - "against_blanket_order": args.against_blanket_order - }) + if args.rm_items: + for row in args.rm_items: + po.append("items", row) + else: + po.append("items", { + "item_code": args.item or args.item_code or "_Test Item", + "warehouse": args.warehouse or "_Test Warehouse - _TC", + "qty": args.qty or 10, + "rate": args.rate or 500, + "schedule_date": add_days(nowdate(), 1), + "include_exploded_items": args.get('include_exploded_items', 1), + "against_blanket_order": args.against_blanket_order + }) + + po.set_missing_values() if not args.do_not_save: po.insert() if not args.do_not_submit: if po.is_subcontracted == "Yes": supp_items = po.get("supplied_items") for d in supp_items: - d.reserve_warehouse = args.warehouse or "_Test Warehouse - _TC" + if not d.reserve_warehouse: + d.reserve_warehouse = args.warehouse or "_Test Warehouse - _TC" po.submit() return po diff --git a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json index d7ea9c1ccc..505ecd84c5 100644 --- a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json +++ b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json @@ -6,21 +6,25 @@ "engine": "InnoDB", "field_order": [ "main_item_code", - "bom_detail_no", + "rm_item_code", + "column_break_3", "stock_uom", + "reserve_warehouse", "conversion_factor", "column_break_6", - "rm_item_code", + "bom_detail_no", "reference_name", - "reserve_warehouse", "section_break2", "rate", "col_break2", "amount", "section_break1", "required_qty", + "supplied_qty", "col_break1", - "supplied_qty" + "returned_qty", + "total_supplied_qty", + "consumed_qty" ], "fields": [ { @@ -125,6 +129,8 @@ "fieldtype": "Float", "in_list_view": 1, "label": "Supplied Qty", + "no_copy": 1, + "print_hide": 1, "read_only": 1 }, { @@ -142,13 +148,42 @@ { "fieldname": "col_break2", "fieldtype": "Column Break" + }, + { + "fieldname": "consumed_qty", + "fieldtype": "Float", + "label": "Consumed Qty", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "returned_qty", + "fieldtype": "Float", + "label": "Returned Qty", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "total_supplied_qty", + "fieldtype": "Float", + "hidden": 1, + "label": "Total Supplied Qty", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" } ], "hide_toolbar": 1, "idx": 1, "istable": 1, "links": [], - "modified": "2020-09-18 17:26:09.703215", + "modified": "2021-06-01 00:41:54.123436", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item Supplied", diff --git a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json index dc00bca5cc..d8c37f5881 100644 --- a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json +++ b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json @@ -6,10 +6,11 @@ "engine": "InnoDB", "field_order": [ "main_item_code", - "description", + "rm_item_code", + "item_name", "bom_detail_no", "col_break1", - "rm_item_code", + "description", "stock_uom", "conversion_factor", "reference_name", @@ -52,7 +53,6 @@ "fieldname": "description", "fieldtype": "Text Editor", "in_global_search": 1, - "in_list_view": 1, "label": "Description", "oldfieldname": "description", "oldfieldtype": "Data", @@ -87,12 +87,13 @@ "read_only": 1 }, { + "columns": 2, "fieldname": "consumed_qty", "fieldtype": "Float", + "in_list_view": 1, "label": "Consumed Qty", "oldfieldname": "consumed_qty", "oldfieldtype": "Currency", - "read_only": 1, "reqd": 1 }, { @@ -183,12 +184,18 @@ { "fieldname": "col_break4", "fieldtype": "Column Break" + }, + { + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name", + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-09-18 17:26:09.703215", + "modified": "2021-05-29 17:22:14.977117", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Receipt Item Supplied", diff --git a/erpnext/buying/report/subcontract_order_summary/__init__.py b/erpnext/buying/report/subcontract_order_summary/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js new file mode 100644 index 0000000000..5ba52f1b21 --- /dev/null +++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js @@ -0,0 +1,45 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Subcontract Order Summary"] = { + "filters": [ + { + label: __("Company"), + fieldname: "company", + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + label: __("From Date"), + fieldname:"from_date", + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1 + }, + { + label: __("To Date"), + fieldname:"to_date", + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1 + }, + { + label: __("Purchase Order"), + fieldname: "name", + fieldtype: "Link", + options: "Purchase Order", + get_query: function() { + return { + filters: { + docstatus: 1, + is_subcontracted: 'Yes', + company: frappe.query_report.get_filter_value('company') + } + } + } + } + ] +}; diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.json b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.json new file mode 100644 index 0000000000..526a8d8ad0 --- /dev/null +++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.json @@ -0,0 +1,32 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2021-05-31 14:43:32.417694", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "modified": "2021-05-31 14:43:32.417694", + "modified_by": "Administrator", + "module": "Buying", + "name": "Subcontract Order Summary", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Purchase Order", + "report_name": "Subcontract Order Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "Stock User" + }, + { + "role": "Purchase Manager" + }, + { + "role": "Purchase User" + } + ] +} \ No newline at end of file diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py new file mode 100644 index 0000000000..8b08d2a284 --- /dev/null +++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py @@ -0,0 +1,158 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +def execute(filters=None): + columns, data = [], [] + columns = get_columns() + data = get_data(filters) + + return columns, data + +def get_data(report_filters): + data = [] + orders = get_subcontracted_orders(report_filters) + + if orders: + supplied_items = get_supplied_items(orders, report_filters) + po_details = prepare_subcontracted_data(orders, supplied_items) + get_subcontracted_data(po_details, data) + + return data + +def get_subcontracted_orders(report_filters): + fields = ['`tabPurchase Order Item`.`parent` as po_id', '`tabPurchase Order Item`.`item_code`', + '`tabPurchase Order Item`.`item_name`', '`tabPurchase Order Item`.`qty`', '`tabPurchase Order Item`.`name`', + '`tabPurchase Order Item`.`received_qty`', '`tabPurchase Order`.`status`'] + + filters = get_filters(report_filters) + + return frappe.get_all('Purchase Order', fields = fields, filters=filters) or [] + +def get_filters(report_filters): + filters = [['Purchase Order', 'docstatus', '=', 1], ['Purchase Order', 'is_subcontracted', '=', 'Yes'], + ['Purchase Order', 'transaction_date', 'between', (report_filters.from_date, report_filters.to_date)]] + + for field in ['name', 'company']: + if report_filters.get(field): + filters.append(['Purchase Order', field, '=', report_filters.get(field)]) + + return filters + +def get_supplied_items(orders, report_filters): + if not orders: + return [] + + fields = ['parent', 'main_item_code', 'rm_item_code', 'required_qty', + 'supplied_qty', 'returned_qty', 'total_supplied_qty', 'consumed_qty', 'reference_name'] + + filters = {'parent': ('in', [d.po_id for d in orders]), 'docstatus': 1} + + supplied_items = {} + for row in frappe.get_all('Purchase Order Item Supplied', fields = fields, filters=filters): + new_key = (row.parent, row.reference_name, row.main_item_code) + + supplied_items.setdefault(new_key, []).append(row) + + return supplied_items + +def prepare_subcontracted_data(orders, supplied_items): + po_details = {} + for row in orders: + key = (row.po_id, row.name, row.item_code) + if key not in po_details: + po_details.setdefault(key, frappe._dict({'po_item': row, 'supplied_items': []})) + + details = po_details[key] + + if supplied_items.get(key): + for supplied_item in supplied_items[key]: + details['supplied_items'].append(supplied_item) + + return po_details + +def get_subcontracted_data(po_details, data): + for key, details in po_details.items(): + res = details.po_item + for index, row in enumerate(details.supplied_items): + if index != 0: + res = {} + + res.update(row) + data.append(res) + +def get_columns(): + return [ + { + "label": _("Id"), + "fieldname": "po_id", + "fieldtype": "Link", + "options": "Purchase Order", + "width": 100 + }, + { + "label": _("Status"), + "fieldname": "status", + "fieldtype": "Data", + "width": 80 + }, + { + "label": _("Subcontracted Item"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 140 + }, + { + "label": _("Qty"), + "fieldname": "qty", + "fieldtype": "Float", + "width": 70 + }, + { + "label": _("Received"), + "fieldname": "received_qty", + "fieldtype": "Float", + "width": 80 + }, + { + "label": _("Supplied Item"), + "fieldname": "rm_item_code", + "fieldtype": "Link", + "options": "Item", + "width": 140 + }, + { + "label": _("Required Qty"), + "fieldname": "required_qty", + "fieldtype": "Float", + "width": 110 + }, + { + "label": _("Supplied Qty"), + "fieldname": "supplied_qty", + "fieldtype": "Float", + "width": 110 + }, + { + "label": _("Returned Qty"), + "fieldname": "returned_qty", + "fieldtype": "Float", + "width": 110 + }, + { + "label": _("Total Supplied"), + "fieldname": "total_supplied_qty", + "fieldtype": "Float", + "width": 120 + }, + { + "label": _("Consumed Qty"), + "fieldname": "consumed_qty", + "fieldtype": "Float", + "width": 110 + }, + ] \ No newline at end of file diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 20f5445725..1907885717 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -11,16 +11,17 @@ from erpnext.accounts.party import get_party_details from erpnext.stock.get_item_details import get_conversion_factor from erpnext.buying.utils import validate_for_items, update_last_purchase_rate 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.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): +from erpnext.controllers.stock_controller import StockController +from erpnext.controllers.subcontracting import Subcontracting + +class BuyingController(StockController, Subcontracting): def get_feed(self): if self.get("supplier_name"): @@ -256,7 +257,7 @@ class BuyingController(StockController): 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'): + if reset_outgoing_rate and frappe.get_cached_value('Item', d.rm_item_code, 'is_stock_item'): rate = get_incoming_rate({ "item_code": d.rm_item_code, "warehouse": self.supplier_warehouse, @@ -298,23 +299,7 @@ class BuyingController(StockController): def create_raw_materials_supplied(self, raw_material_table): if self.is_subcontracted=="Yes": - parent_items = [] - backflush_raw_materials_based_on = frappe.db.get_single_value("Buying Settings", - "backflush_raw_materials_of_subcontract_based_on") - if (self.doctype == 'Purchase Receipt' and - backflush_raw_materials_based_on != 'BOM'): - self.update_raw_materials_supplied_based_on_stock_entries() - else: - for item in self.get("items"): - if self.doctype in ["Purchase Receipt", "Purchase Invoice"]: - item.rm_supp_cost = 0.0 - if item.bom and item.item_code in self.sub_contracted_items: - self.update_raw_materials_supplied_based_on_bom(item, raw_material_table) - - if [item.item_code, item.name] not in parent_items: - parent_items.append([item.item_code, item.name]) - - self.cleanup_raw_materials_supplied(parent_items, raw_material_table) + self.set_materials_for_subcontracted_items(raw_material_table) elif self.doctype in ["Purchase Receipt", "Purchase Invoice"]: for item in self.get("items"): @@ -323,176 +308,6 @@ class BuyingController(StockController): if self.is_subcontracted == "No" and self.get("supplied_items"): self.set('supplied_items', []) - def update_raw_materials_supplied_based_on_stock_entries(self): - self.set('supplied_items', []) - - purchase_orders = set(d.purchase_order for d in self.items) - - # qty of raw materials backflushed (for each item per purchase order) - backflushed_raw_materials_map = get_backflushed_subcontracted_raw_materials(purchase_orders) - - # qty of "finished good" item yet to be received - qty_to_be_received_map = get_qty_to_be_received(purchase_orders) - - for item in self.get('items'): - if not item.purchase_order: - continue - - # reset raw_material cost - item.rm_supp_cost = 0 - - # qty of raw materials transferred to the supplier - transferred_raw_materials = get_subcontracted_raw_materials_from_se(item.purchase_order, item.item_code) - - non_stock_items = get_non_stock_items(item.purchase_order, item.item_code) - - item_key = '{}{}'.format(item.item_code, item.purchase_order) - - fg_yet_to_be_received = qty_to_be_received_map.get(item_key) - - if not fg_yet_to_be_received: - frappe.throw(_("Row #{0}: Item {1} is already fully received in Purchase Order {2}") - .format(item.idx, frappe.bold(item.item_code), - frappe.utils.get_link_to_form("Purchase Order", item.purchase_order)), - title=_("Limit Crossed")) - - transferred_batch_qty_map = get_transferred_batch_qty_map(item.purchase_order, item.item_code) - # backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code) - - for raw_material in transferred_raw_materials + non_stock_items: - rm_item_key = (raw_material.rm_item_code, item.item_code, item.purchase_order) - raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {}) - - consumed_qty = raw_material_data.get('qty', 0) - consumed_serial_nos = raw_material_data.get('serial_no', '') - consumed_batch_nos = raw_material_data.get('batch_nos', '') - - transferred_qty = raw_material.qty - - rm_qty_to_be_consumed = transferred_qty - consumed_qty - - # backflush all remaining transferred qty in the last Purchase Receipt - if fg_yet_to_be_received == item.qty: - qty = rm_qty_to_be_consumed - else: - qty = (rm_qty_to_be_consumed / fg_yet_to_be_received) * item.qty - - if frappe.get_cached_value('UOM', raw_material.stock_uom, 'must_be_whole_number'): - qty = frappe.utils.ceil(qty) - - if qty > rm_qty_to_be_consumed: - qty = rm_qty_to_be_consumed - - if not qty: continue - - if raw_material.serial_nos: - set_serial_nos(raw_material, consumed_serial_nos, qty) - - if raw_material.batch_nos: - backflushed_batch_qty_map = raw_material_data.get('consumed_batch', {}) - - batches_qty = get_batches_with_qty(raw_material.rm_item_code, raw_material.main_item_code, - qty, transferred_batch_qty_map, backflushed_batch_qty_map, item.purchase_order) - - for batch_data in batches_qty: - qty = batch_data['qty'] - raw_material.batch_no = batch_data['batch'] - if qty > 0: - self.append_raw_material_to_be_backflushed(item, raw_material, qty) - else: - self.append_raw_material_to_be_backflushed(item, raw_material, 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_row.item_code - - rm.reference_name = fg_item_row.name - rm.required_qty = qty - rm.consumed_qty = qty - - def update_raw_materials_supplied_based_on_bom(self, item, raw_material_table): - exploded_item = 1 - if hasattr(item, 'include_exploded_items'): - exploded_item = item.get('include_exploded_items') - - bom_items = get_items_from_bom(item.item_code, item.bom, exploded_item) - - used_alternative_items = [] - 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 - items = list(set([d.item_code for d in bom_items])) - item_wh = frappe._dict(frappe.db.sql("""select i.item_code, id.default_warehouse - from `tabItem` i, `tabItem Default` id - where id.parent=i.name and id.company=%s and i.name in ({0})""" - .format(", ".join(["%s"] * len(items))), [self.company] + items)) - - for bom_item in bom_items: - if self.doctype == "Purchase Order": - reserve_warehouse = bom_item.source_warehouse or item_wh.get(bom_item.item_code) - if frappe.db.get_value("Warehouse", reserve_warehouse, "company") != self.company: - reserve_warehouse = None - - conversion_factor = item.conversion_factor - 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 - bom_item.item_name = alternative_item_data.item_name - bom_item.stock_uom = alternative_item_data.stock_uom - conversion_factor = alternative_item_data.conversion_factor - bom_item.description = alternative_item_data.description - - # check if exists - exists = 0 - for d in self.get(raw_material_table): - if d.main_item_code == item.item_code and d.rm_item_code == bom_item.item_code \ - and d.reference_name == item.name: - rm, exists = d, 1 - break - - if not exists: - rm = self.append(raw_material_table, {}) - - required_qty = flt(flt(bom_item.qty_consumed_per_unit) * (flt(item.qty) + getattr(item, 'rejected_qty', 0)) * - flt(conversion_factor), rm.precision("required_qty")) - rm.reference_name = item.name - rm.bom_detail_no = bom_item.name - rm.main_item_code = item.item_code - rm.rm_item_code = bom_item.item_code - rm.stock_uom = bom_item.stock_uom - rm.required_qty = required_qty - rm.rate = bom_item.rate - rm.conversion_factor = conversion_factor - - if self.doctype in ["Purchase Receipt", "Purchase Invoice"]: - rm.consumed_qty = required_qty - 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 - 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""" - delete_list = [] - for d in self.get(raw_material_table): - if [d.main_item_code, d.reference_name] not in parent_items: - # mark for deletion from doclist - delete_list.append(d) - - # delete from doclist - if delete_list: - rm_supplied_details = self.get(raw_material_table) - self.set(raw_material_table, []) - for d in rm_supplied_details: - if d not in delete_list: - self.append(raw_material_table, d) - @property def sub_contracted_items(self): if not hasattr(self, "_sub_contracted_items"): @@ -867,104 +682,6 @@ 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" - - bom_items = frappe.db.sql("""select t2.item_code, t2.name, - t2.rate, t2.stock_uom, t2.source_warehouse, t2.description, - t2.stock_qty / ifnull(t1.quantity, 1) as qty_consumed_per_unit - from - `tabBOM` t1, `tab{0}` t2, tabItem t3 - where - t2.parent = t1.name and t1.item = %s - and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s - and t2.sourced_by_supplier = 0 - and t2.item_code = t3.name""".format(doctype), - (item_code, bom), as_dict=1) - - if not bom_items: - msgprint(_("Specified BOM {0} does not exist for Item {1}").format(bom, item_code), raise_exception=1) - - return bom_items - -def get_subcontracted_raw_materials_from_se(purchase_order, fg_item): - common_query = """ - SELECT - sed.item_code AS rm_item_code, - SUM(sed.qty) AS qty, - sed.description, - sed.stock_uom, - sed.subcontracted_item AS main_item_code, - {serial_no_concat_syntax} AS serial_nos, - {batch_no_concat_syntax} AS batch_nos - FROM `tabStock Entry` se,`tabStock Entry Detail` sed - WHERE - se.name = sed.parent - AND se.docstatus=1 - AND se.purpose='Send to Subcontractor' - AND se.purchase_order = %s - AND IFNULL(sed.t_warehouse, '') != '' - AND IFNULL(sed.subcontracted_item, '') in ('', %s) - GROUP BY sed.item_code, sed.subcontracted_item - """ - raw_materials = frappe.db.multisql({ - 'mariadb': common_query.format( - serial_no_concat_syntax="GROUP_CONCAT(sed.serial_no)", - batch_no_concat_syntax="GROUP_CONCAT(sed.batch_no)" - ), - 'postgres': common_query.format( - serial_no_concat_syntax="STRING_AGG(sed.serial_no, ',')", - batch_no_concat_syntax="STRING_AGG(sed.batch_no, ',')" - ) - }, (purchase_order, fg_item), as_dict=1) - - return raw_materials - -def get_backflushed_subcontracted_raw_materials(purchase_orders): - purchase_receipts = frappe.get_all("Purchase Receipt Item", - fields = ["purchase_order", "item_code", "name", "parent"], - filters={"docstatus": 1, "purchase_order": ("in", list(purchase_orders))}) - - distinct_purchase_receipts = {} - for pr in purchase_receipts: - key = (pr.purchase_order, pr.item_code, pr.parent) - distinct_purchase_receipts.setdefault(key, []).append(pr.name) - - backflushed_raw_materials_map = frappe._dict() - for args, references in iteritems(distinct_purchase_receipts): - purchase_receipt_supplied_items = get_supplied_items(args[1], args[2], references) - - for data in purchase_receipt_supplied_items: - pr_key = (data.rm_item_code, data.main_item_code, args[0]) - if pr_key not in backflushed_raw_materials_map: - backflushed_raw_materials_map.setdefault(pr_key, frappe._dict({ - "qty": 0.0, - "serial_no": [], - "batch_no": [], - "consumed_batch": {} - })) - - row = backflushed_raw_materials_map.get(pr_key) - row.qty += data.consumed_qty - - for field in ["serial_no", "batch_no"]: - if data.get(field): - row[field].append(data.get(field)) - - if data.get("batch_no"): - if data.get("batch_no") in row.consumed_batch: - row.consumed_batch[data.get("batch_no")] += data.consumed_qty - else: - row.consumed_batch[data.get("batch_no")] = data.consumed_qty - - return backflushed_raw_materials_map - -def get_supplied_items(item_code, purchase_receipt, references): - return frappe.get_all("Purchase Receipt Item Supplied", - fields=["rm_item_code", "main_item_code", "consumed_qty", "serial_no", "batch_no"], - filters={"main_item_code": item_code, "parent": purchase_receipt, "reference_name": ("in", references)}) - def get_asset_item_details(asset_items): asset_items_data = {} for d in frappe.get_all('Item', fields = ["name", "auto_create_assets", "asset_naming_series"], @@ -996,135 +713,3 @@ def validate_item_type(doc, fieldname, message): error_message = _("Following item {0} is not marked as {1} item. You can enable them as {1} item from its Item master").format(items, message) frappe.throw(error_message) - -def get_qty_to_be_received(purchase_orders): - return frappe._dict(frappe.db.sql(""" - SELECT CONCAT(poi.`item_code`, poi.`parent`) AS item_key, - SUM(poi.`qty`) - SUM(poi.`received_qty`) AS qty_to_be_received - FROM `tabPurchase Order Item` poi - WHERE - poi.`parent` in %s - GROUP BY poi.`item_code`, poi.`parent` - HAVING SUM(poi.`qty`) > SUM(poi.`received_qty`) - """, (purchase_orders))) - -def get_non_stock_items(purchase_order, fg_item_code): - return frappe.db.sql(""" - SELECT - pois.main_item_code, - pois.rm_item_code, - item.description, - pois.required_qty AS qty, - pois.rate, - 1 as non_stock_item, - pois.stock_uom - FROM `tabPurchase Order Item Supplied` pois, `tabItem` item - WHERE - pois.`rm_item_code` = item.`name` - AND item.is_stock_item = 0 - AND pois.`parent` = %s - AND pois.`main_item_code` = %s - """, (purchase_order, fg_item_code), as_dict=1) - - -def set_serial_nos(raw_material, consumed_serial_nos, qty): - serial_nos = set(get_serial_nos(raw_material.serial_nos)) - \ - set(get_serial_nos(consumed_serial_nos)) - if serial_nos and qty <= len(serial_nos): - raw_material.serial_no = '\n'.join(list(serial_nos)[0:frappe.utils.cint(qty)]) - -def get_transferred_batch_qty_map(purchase_order, fg_item): - # returns - # { - # (item_code, fg_code): { - # batch1: 10, # qty - # batch2: 16 - # }, - # } - transferred_batch_qty_map = {} - transferred_batches = frappe.db.sql(""" - SELECT - sed.batch_no, - SUM(sed.qty) AS qty, - sed.item_code, - sed.subcontracted_item - FROM `tabStock Entry` se,`tabStock Entry Detail` sed - WHERE - se.name = sed.parent - AND se.docstatus=1 - AND se.purpose='Send to Subcontractor' - AND se.purchase_order = %s - AND ifnull(sed.subcontracted_item, '') in ('', %s) - AND sed.batch_no IS NOT NULL - GROUP BY - sed.batch_no, - sed.item_code - """, (purchase_order, fg_item), as_dict=1) - - for batch_data in transferred_batches: - key = ((batch_data.item_code, fg_item) - if batch_data.subcontracted_item else (batch_data.item_code, purchase_order)) - transferred_batch_qty_map.setdefault(key, OrderedDict()) - transferred_batch_qty_map[key][batch_data.batch_no] = batch_data.qty - - return transferred_batch_qty_map - -def get_backflushed_batch_qty_map(purchase_order, fg_item): - # returns - # { - # (item_code, fg_code): { - # batch1: 10, # qty - # batch2: 16 - # }, - # } - backflushed_batch_qty_map = {} - backflushed_batches = frappe.db.sql(""" - SELECT - pris.batch_no, - SUM(pris.consumed_qty) AS qty, - pris.rm_item_code AS item_code - FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri, `tabPurchase Receipt Item Supplied` pris - WHERE - pr.name = pri.parent - AND pri.parent = pris.parent - AND pri.purchase_order = %s - AND pri.item_code = pris.main_item_code - AND pr.docstatus = 1 - AND pris.main_item_code = %s - AND pris.batch_no IS NOT NULL - GROUP BY - pris.rm_item_code, pris.batch_no - """, (purchase_order, fg_item), as_dict=1) - - for batch_data in backflushed_batches: - backflushed_batch_qty_map.setdefault((batch_data.item_code, fg_item), {}) - backflushed_batch_qty_map[(batch_data.item_code, fg_item)][batch_data.batch_no] = batch_data.qty - - return backflushed_batch_qty_map - -def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty_map, backflushed_batches, po): - # Returns available batches to be backflushed based on requirements - transferred_batches = transferred_batch_qty_map.get((item_code, fg_item), {}) - if not transferred_batches: - transferred_batches = transferred_batch_qty_map.get((item_code, po), {}) - - available_batches = [] - - for (batch, transferred_qty) in transferred_batches.items(): - backflushed_qty = backflushed_batches.get(batch, 0) - available_qty = transferred_qty - backflushed_qty - - if available_qty >= required_qty: - available_batches.append({'batch': batch, 'qty': required_qty}) - break - elif available_qty != 0: - available_batches.append({'batch': batch, 'qty': available_qty}) - required_qty -= available_qty - - for row in available_batches: - if backflushed_batches.get(row.get('batch'), 0) > 0: - backflushed_batches[row.get('batch')] += row.get('qty') - else: - backflushed_batches[row.get('batch')] = row.get('qty') - - return available_batches diff --git a/erpnext/controllers/subcontracting.py b/erpnext/controllers/subcontracting.py new file mode 100644 index 0000000000..fe775766da --- /dev/null +++ b/erpnext/controllers/subcontracting.py @@ -0,0 +1,342 @@ +from __future__ import unicode_literals + +import frappe +from frappe import _ +from frappe.utils import flt, cint +from collections import defaultdict +from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + +class Subcontracting(object): + def set_materials_for_subcontracted_items(self, raw_material_table): + if self.doctype == 'Purchase Invoice' and not self.update_stock: + return + + self.raw_material_table = raw_material_table + self.identify_change_in_item_table() + self.prepare_supplied_items() + self.validate_consumed_qty() + + def prepare_supplied_items(self): + self.initialized_fields() + self.get_purchase_orders() + self.get_pending_qty_to_receive() + self.get_available_materials() + self.remove_changed_rows() + self.set_supplied_items() + + def initialized_fields(self): + self.available_materials = frappe._dict() + self.alternative_item_details = frappe._dict() + self.get_backflush_based_on() + + def get_backflush_based_on(self): + self.backflush_based_on = frappe.db.get_single_value("Buying Settings", + "backflush_raw_materials_of_subcontract_based_on") + + def get_purchase_orders(self): + self.purchase_orders = [] + + if self.doctype == 'Purchase Order': + return + + self.purchase_orders = [d.purchase_order for d in self.items if d.purchase_order] + + def identify_change_in_item_table(self): + self.changed_name = [] + + if self.doctype == 'Purchase Order' or not self.get(self.raw_material_table): + self.set(self.raw_material_table, []) + return + + item_dict = self.get_data_before_save() + if not item_dict: + return True + + for n_row in self.items: + if (n_row.name not in item_dict) or (n_row.item_code, n_row.qty) != item_dict[n_row.name]: + self.changed_name.append(n_row.name) + + if item_dict.get(n_row.name): + del item_dict[n_row.name] + + self.changed_name.extend(item_dict.keys()) + + def get_data_before_save(self): + item_dict = {} + if self.doctype == 'Purchase Receipt' and self._doc_before_save: + for row in self._doc_before_save.get('items'): + item_dict[row.name] = (row.item_code, row.qty) + + return item_dict + + def get_available_materials(self): + ''' Get the available raw materials which has been transferred to the supplier. + available_materials = { + (item_code, subcontracted_item, purchase_order): { + 'qty': 1, 'serial_no': [ABC], 'batch_no': {'batch1': 1}, 'data': item_details + } + } + ''' + if not self.purchase_orders: + return + + for row in self.get_transferred_items(): + key = (row.rm_item_code, row.main_item_code, row.purchase_order) + + if key not in self.available_materials: + self.available_materials.setdefault(key, frappe._dict({'qty': 0, 'serial_no': [], + 'batch_no': defaultdict(float), 'item_details': row, 'po_details': []}) + ) + + details = self.available_materials[key] + details.qty += row.qty + details.po_details.append(row.po_detail) + + if row.serial_no: + details.serial_no.extend(get_serial_nos(row.serial_no)) + + if row.batch_no: + details.batch_no[row.batch_no] += row.qty + + self.set_alternative_item_details(row) + + for doctype in ['Purchase Receipt', 'Purchase Invoice']: + self.remove_consumed_materials(doctype) + + def remove_consumed_materials(self, doctype, return_consumed_items=False): + '''Deduct the consumed materials from the available materials.''' + + pr_items = self.get_received_items(doctype) + if not pr_items: + return ([], {}) if return_consumed_items else None + + pr_items = {d.name: d.get(self.get('po_field') or 'purchase_order') for d in pr_items} + consumed_materials = self.get_consumed_items(doctype, pr_items.keys()) + + if return_consumed_items: + return (consumed_materials, pr_items) + + for row in consumed_materials: + key = (row.rm_item_code, row.main_item_code, pr_items.get(row.reference_name)) + if not self.available_materials.get(key): + continue + + self.available_materials[key]['qty'] -= row.consumed_qty + if row.serial_no: + self.available_materials[key]['serial_no'] = list( + set(self.available_materials[key]['serial_no']) - set(get_serial_nos(row.serial_no)) + ) + + if row.batch_no: + self.available_materials[key]['batch_no'][row.batch_no] -= row.consumed_qty + + def get_transferred_items(self): + fields = ['`tabStock Entry`.`purchase_order`'] + alias_dict = {'item_code': 'rm_item_code', 'subcontracted_item': 'main_item_code', 'basic_rate': 'rate'} + + child_table_fields = ['item_code', 'item_name', 'description', 'qty', 'basic_rate', 'amount', + 'serial_no', 'uom', 'subcontracted_item', 'stock_uom', 'batch_no', 'conversion_factor', + 's_warehouse', 't_warehouse', 'item_group', 'po_detail'] + + if self.backflush_based_on == 'BOM': + child_table_fields.append('original_item') + + for field in child_table_fields: + fields.append(f'`tabStock Entry Detail`.`{field}` As {alias_dict.get(field, field)}') + + filters = [['Stock Entry', 'docstatus', '=', 1], ['Stock Entry', 'purpose', '=', 'Send to Subcontractor'], + ['Stock Entry', 'purchase_order', 'in', self.purchase_orders]] + + return frappe.get_all('Stock Entry', fields = fields, filters=filters) + + def get_received_items(self, doctype): + fields = [] + self.po_field = 'purchase_order' if doctype == 'Purchase Receipt' else 'po_detail' + + for field in ['name', self.po_field, 'parent']: + fields.append(f'`tab{doctype} Item`.`{field}`') + + filters = [[doctype, 'docstatus', '=', 1], [f'{doctype} Item', self.po_field, 'in', self.purchase_orders]] + if doctype == 'Purchase Invoice': + filters.append(['Purchase Invoice', 'update_stock', "=", 1]) + + return frappe.get_all(f'{doctype}', fields = fields, filters = filters) + + def get_consumed_items(self, doctype, pr_items): + return frappe.get_all(f'{doctype} Item Supplied', + fields = ['serial_no', 'rm_item_code', 'reference_name', 'batch_no', 'consumed_qty', 'main_item_code'], + filters = {'docstatus': 1, 'reference_name': ('in', list(pr_items))}) + + def set_alternative_item_details(self, row): + if row.get('original_item'): + self.alternative_item_details[row.get('original_item')] = row + + def get_pending_qty_to_receive(self): + '''Get qty to be received against the purchase order.''' + + self.qty_to_be_received = defaultdict(float) + + if self.doctype != 'Purchase Order' and self.backflush_based_on != 'BOM' and self.purchase_orders: + for row in frappe.get_all('Purchase Order Item', + fields = ['item_code', '(qty - received_qty) as qty', 'parent', 'name'], + filters = {'docstatus': 1, 'parent': ('in', self.purchase_orders)}): + + self.qty_to_be_received[(row.item_code, row.parent)] += row.qty + + def get_materials_from_bom(self, item_code, bom_no, exploded_item=0): + doctype = 'BOM Item' if not exploded_item else 'BOM Explosion Item' + fields = [f'`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit'] + + alias_dict = {'item_code': 'rm_item_code', 'name': 'bom_detail_no', 'source_warehouse': 'reserve_warehouse'} + for field in ['item_code', 'name', 'rate', 'stock_uom', + 'source_warehouse', 'description', 'item_name', 'stock_uom']: + fields.append(f'`tab{doctype}`.`{field}` As {alias_dict.get(field, field)}') + + filters = [[doctype, 'parent', '=', bom_no], [doctype, 'docstatus', '=', 1], + ['BOM', 'item', '=', item_code], [doctype, 'sourced_by_supplier', '=', 0]] + + return frappe.get_all('BOM', fields = fields, filters=filters, order_by = f'`tab{doctype}`.`idx`') or [] + + def remove_changed_rows(self): + if not self.changed_name: + return + + i=1 + self.set(self.raw_material_table, []) + for d in self._doc_before_save.supplied_items: + if d.reference_name in self.changed_name: + continue + + d.idx = i + self.append('supplied_items', d) + + i += 1 + + def set_supplied_items(self): + self.bom_items = {} + + has_supplied_items = True if self.get(self.raw_material_table) else False + for row in self.items: + if (self.doctype != 'Purchase Order' and ((self.changed_name and row.name not in self.changed_name) + or (has_supplied_items and not self.changed_name))): + continue + + if self.doctype == 'Purchase Order' or self.backflush_based_on == 'BOM': + for bom_item in self.get_materials_from_bom(row.item_code, row.bom, row.get('include_exploded_items')): + qty = (flt(bom_item.qty_consumed_per_unit) * flt(row.qty) * row.conversion_factor) + bom_item.main_item_code = row.item_code + self.update_reserve_warehouse(bom_item, row) + self.set_alternative_item(bom_item) + self.add_supplied_item(row, bom_item, qty) + + elif self.backflush_based_on != 'BOM': + for key, transfer_item in self.available_materials.items(): + if (key[1], key[2]) == (row.item_code, row.purchase_order) and transfer_item.qty > 0: + qty = self.get_qty_based_on_material_transfer(row, transfer_item) or 0 + transfer_item.qty -= qty + self.add_supplied_item(row, transfer_item.get('item_details'), qty) + + if self.qty_to_be_received: + self.qty_to_be_received[(row.item_code, row.purchase_order)] -= row.qty + + def update_reserve_warehouse(self, row, item): + if self.doctype == 'Purchase Order': + row.reserve_warehouse = (self.set_reserve_warehouse or item.warehouse) + + def get_qty_based_on_material_transfer(self, item_row, transfer_item): + key = (item_row.item_code, item_row.purchase_order) + + if self.qty_to_be_received == item_row.qty: + return transfer_item.qty + + if self.qty_to_be_received: + qty = (flt(item_row.qty) * flt(transfer_item.qty)) / flt(self.qty_to_be_received.get(key, 0)) + if (transfer_item.serial_no or frappe.get_cached_value('UOM', + transfer_item.item_details.stock_uom, 'must_be_whole_number')): + return frappe.utils.ceil(qty) + + return qty + + def set_alternative_item(self, bom_item): + if self.alternative_item_details.get(bom_item.rm_item_code): + bom_item.update(self.alternative_item_details[bom_item.rm_item_code]) + + def add_supplied_item(self, item_row, bom_item, qty): + bom_item.conversion_factor = item_row.conversion_factor + rm_obj = self.append(self.raw_material_table, bom_item) + rm_obj.reference_name = item_row.name + + if self.doctype == 'Purchase Order': + rm_obj.required_qty = qty + else: + self.set_batch_nos(bom_item, item_row, rm_obj, qty) + + def set_batch_nos(self, bom_item, item_row, rm_obj, qty): + key = (rm_obj.rm_item_code, item_row.item_code, item_row.purchase_order) + + if (self.available_materials.get(key) and self.available_materials[key]['batch_no']): + for batch_no, batch_qty in self.available_materials[key]['batch_no'].items(): + if batch_qty >= qty: + self.set_batch_no_as_per_qty(item_row, rm_obj, batch_no, qty) + self.available_materials[key]['batch_no'][batch_no] -= qty + return + + elif qty > 0 and batch_qty > 0: + qty -= batch_qty + new_rm_obj = self.append(self.raw_material_table, bom_item) + new_rm_obj.reference_name = item_row.name + self.set_batch_no_as_per_qty(item_row, new_rm_obj, batch_no, batch_qty) + self.available_materials[key]['batch_no'][batch_no] = 0 + else: + rm_obj.required_qty = qty + rm_obj.consumed_qty = qty + self.set_serial_nos(item_row, rm_obj) + + def set_batch_no_as_per_qty(self, item_row, rm_obj, batch_no, qty): + rm_obj.update({'consumed_qty': qty, 'batch_no': batch_no, 'required_qty': qty}) + self.set_serial_nos(item_row, rm_obj) + + def set_serial_nos(self, item_row, rm_obj): + key = (rm_obj.rm_item_code, item_row.item_code, item_row.purchase_order) + if (self.available_materials.get(key) and self.available_materials[key]['serial_no']): + used_serial_nos = self.available_materials[key]['serial_no'][0: cint(rm_obj.consumed_qty)] + rm_obj.serial_no = '\n'.join(used_serial_nos) + + # Removed the used serial nos from the list + for sn in used_serial_nos: + self.available_materials[key]['serial_no'].remove(sn) + + def set_consumed_qty_in_po(self): + if self.is_subcontracted != 'Yes': + return + + self.get_purchase_orders() + consumed_items, pr_items = self.remove_consumed_materials(self.doctype, return_consumed_items=True) + + itemwise_consumed_qty = defaultdict(float) + for row in consumed_items: + key = (row.rm_item_code, row.main_item_code, pr_items.get(row.reference_name)) + itemwise_consumed_qty[key] += row.consumed_qty + + self.update_consumed_qty_in_po(itemwise_consumed_qty) + + def update_consumed_qty_in_po(self, itemwise_consumed_qty): + fields = ['main_item_code', 'rm_item_code', 'parent', 'supplied_qty', 'name'] + filters = {'docstatus': 1, 'parent': ('in', self.purchase_orders)} + + for row in frappe.get_all('Purchase Order Item Supplied', fields = fields, filters=filters, order_by='idx'): + key = (row.rm_item_code, row.main_item_code, row.parent) + consumed_qty = itemwise_consumed_qty.get(key, 0) + + if row.supplied_qty < consumed_qty: + consumed_qty = row.supplied_qty + + itemwise_consumed_qty[key] -= consumed_qty + frappe.db.set_value('Purchase Order Item Supplied', row.name, 'consumed_qty', consumed_qty) + + def validate_consumed_qty(self): + for row in self.get(self.raw_material_table): + if flt(row.consumed_qty) == 0.0 and row.get('serial_no'): + msg = f'Row {row.idx}: the consumed qty cannot be zero for the item {frappe.bold(row.rm_item_code)}' + + frappe.throw(_(msg),title=_('Consumed Items Qty Check')) \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index e1cca9e3ef..42b23f223d 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -12,6 +12,7 @@ from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update from six import string_types from erpnext.stock.doctype.item.test_item import make_item from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order +from erpnext.tests.test_subcontracting import set_backflush_based_on test_records = frappe.get_test_records('BOM') @@ -160,6 +161,7 @@ class TestBOM(unittest.TestCase): def test_subcontractor_sourced_item(self): item_code = "_Test Subcontracted FG Item 1" + set_backflush_based_on('Material Transferred for Subcontract') if not frappe.db.exists('Item', item_code): make_item(item_code, { diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 0514bd2394..43642013ce 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -54,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() @@ -115,7 +115,7 @@ class Bin(Document): #Get Transferred Entries materials_transferred = frappe.db.sql(""" select - ifnull(sum(transfer_qty),0) + ifnull(sum(CASE WHEN se.is_return = 1 THEN (transfer_qty * -1) ELSE transfer_qty END),0) from `tabStock Entry` se, `tabStock Entry Detail` sed, `tabPurchase Order` po where diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.py b/erpnext/stock/doctype/item_alternative/test_item_alternative.py index d5700fe514..8f76844bde 100644 --- a/erpnext/stock/doctype/item_alternative/test_item_alternative.py +++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.py @@ -18,6 +18,9 @@ class TestItemAlternative(unittest.TestCase): make_items() def test_alternative_item_for_subcontract_rm(self): + frappe.db.set_value('Buying Settings', None, + 'backflush_raw_materials_of_subcontract_based_on', 'BOM') + create_stock_reconciliation(item_code='Alternate Item For A RW 1', warehouse='_Test Warehouse - _TC', qty=5, rate=2000) create_stock_reconciliation(item_code='Test FG A RW 2', warehouse='_Test Warehouse - _TC', @@ -65,6 +68,8 @@ class TestItemAlternative(unittest.TestCase): status = True self.assertEqual(status, True) + frappe.db.set_value('Buying Settings', None, + 'backflush_raw_materials_of_subcontract_based_on', 'Material Transferred for Subcontract') def test_alternative_item_for_production_rm(self): create_stock_reconciliation(item_code='Alternate Item For A RW 1', diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index befdad9692..887b15a211 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -41,6 +41,8 @@ frappe.ui.form.on("Purchase Receipt", { } }); + frm.set_df_property('supplied_items', 'cannot_add_rows', 1); + }, onload: function(frm) { erpnext.queries.setup_queries(frm, "Warehouse", function() { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index ad350d344f..44fb736304 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -514,8 +514,7 @@ "oldfieldname": "pr_raw_material_details", "oldfieldtype": "Table", "options": "Purchase Receipt Item Supplied", - "print_hide": 1, - "read_only": 1 + "print_hide": 1 }, { "fieldname": "section_break0", @@ -1149,7 +1148,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2021-04-19 01:01:00.754119", + "modified": "2021-05-25 00:15:12.239017", "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 83ba324495..b8580f95a3 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -202,6 +202,7 @@ class PurchaseReceipt(BuyingController): self.make_gl_entries() self.repost_future_sle_and_gle() + self.set_consumed_qty_in_po() def check_next_docstatus(self): submit_rv = frappe.db.sql("""select t1.name @@ -233,6 +234,7 @@ class PurchaseReceipt(BuyingController): self.repost_future_sle_and_gle() self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation') self.delete_auto_created_batches() + self.set_consumed_qty_in_po() @frappe.whitelist() 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 8d9b675bed..95096d77d7 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -335,6 +335,10 @@ class TestPurchaseReceipt(unittest.TestCase): se2.cancel() se3.cancel() po.reload() + pr2.load_from_db() + pr2.cancel() + + po.load_from_db() po.cancel() def test_serial_no_supplier(self): diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 1a25994b24..6708393027 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -1079,6 +1079,10 @@ erpnext.stock.select_batch_and_serial_no = (frm, item) => { } function attach_bom_items(bom_no) { + if (!bom_no) { + return + } + if (check_should_not_attach_bom_items(bom_no)) return frappe.db.get_doc("BOM",bom_no).then(bom => { const {name, items} = bom diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index a0b5457dd7..523d332b8f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -74,7 +74,8 @@ "total_amount", "job_card", "amended_from", - "credit_note" + "credit_note", + "is_return" ], "fields": [ { @@ -611,6 +612,16 @@ "fieldname": "apply_putaway_rule", "fieldtype": "Check", "label": "Apply Putaway Rule" + }, + { + "default": "0", + "fieldname": "is_return", + "fieldtype": "Check", + "hidden": 1, + "label": "Is Return", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "icon": "fa fa-file-text", @@ -618,7 +629,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-05-24 11:32:23.904307", + "modified": "2021-05-26 17:07:58.015737", "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 560ceaa917..213280870a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -97,8 +97,7 @@ class StockEntry(StockController): update_serial_nos_after_submit(self, "items") self.update_work_order() self.validate_purchase_order() - if self.purchase_order and self.purpose == "Send to Subcontractor": - self.update_purchase_order_supplied_items() + self.update_purchase_order_supplied_items() self.make_gl_entries() @@ -117,9 +116,7 @@ class StockEntry(StockController): self.set_material_request_transfer_status('Completed') def on_cancel(self): - - if self.purchase_order and self.purpose == "Send to Subcontractor": - self.update_purchase_order_supplied_items() + self.update_purchase_order_supplied_items() if self.work_order and self.purpose == "Material Consumption for Manufacture": self.validate_work_order_status() @@ -1347,7 +1344,7 @@ class StockEntry(StockController): 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"]: + "expense_account", "description", "item_name", "serial_no", "batch_no"]: if item_dict[d].get(field): se_child.set(field, item_dict[d].get(field)) @@ -1400,33 +1397,26 @@ class StockEntry(StockController): .format(item.batch_no, item.item_code)) def update_purchase_order_supplied_items(self): - #Get PO Supplied Items Details - item_wh = frappe._dict(frappe.db.sql(""" - select rm_item_code, reserve_warehouse - from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup - where po.name = poitemsup.parent - and po.name = %s""", self.purchase_order)) + if (self.purchase_order and + (self.purpose in ['Send to Subcontractor', 'Material Transfer'] or self.is_return)): - #Update Supplied Qty in PO Supplied Items + #Get PO Supplied Items Details + item_wh = frappe._dict(frappe.db.sql(""" + select rm_item_code, reserve_warehouse + from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup + where po.name = poitemsup.parent + and po.name = %s""", self.purchase_order)) - frappe.db.sql("""UPDATE `tabPurchase Order Item Supplied` pos - SET - pos.supplied_qty = IFNULL((SELECT ifnull(sum(transfer_qty), 0) - FROM - `tabStock Entry Detail` sed, `tabStock Entry` se - WHERE - pos.name = sed.po_detail AND pos.rm_item_code = sed.item_code - AND pos.parent = se.purchase_order AND sed.docstatus = 1 - AND se.name = sed.parent and se.purchase_order = %(po)s - ), 0) - WHERE pos.docstatus = 1 and pos.parent = %(po)s""", {"po": self.purchase_order}) + supplied_items = get_supplied_items(self.purchase_order) + for name, item in supplied_items.items(): + frappe.db.set_value('Purchase Order Item Supplied', name, item) - #Update reserved sub contracted quantity in bin based on Supplied Item Details and - for d in self.get("items"): - item_code = d.get('original_item') or d.get('item_code') - reserve_warehouse = item_wh.get(item_code) - stock_bin = get_bin(item_code, reserve_warehouse) - stock_bin.update_reserved_qty_for_sub_contracting() + #Update reserved sub contracted quantity in bin based on Supplied Item Details and + for d in self.get("items"): + item_code = d.get('original_item') or d.get('item_code') + reserve_warehouse = item_wh.get(item_code) + stock_bin = get_bin(item_code, reserve_warehouse) + stock_bin.update_reserved_qty_for_sub_contracting() def update_so_in_serial_number(self): so_name, item_code = frappe.db.get_value("Work Order", self.work_order, ["sales_order", "production_item"]) @@ -1480,7 +1470,7 @@ class StockEntry(StockController): cond += """ WHEN (parent = %s and name = %s) THEN %s """ %(frappe.db.escape(data[0]), frappe.db.escape(data[1]), transferred_qty) - if cond and stock_entries_child_list: + if stock_entries_child_list: frappe.db.sql(""" UPDATE `tabStock Entry Detail` SET transferred_qty = CASE {cond} END @@ -1751,3 +1741,30 @@ def validate_sample_quantity(item_code, sample_quantity, qty, batch_no = None): format(max_retain_qty, batch_no, item_code), alert=True) sample_quantity = qty_diff return sample_quantity + +def get_supplied_items(purchase_order): + fields = ['`tabStock Entry Detail`.`transfer_qty`', '`tabStock Entry`.`is_return`', + '`tabStock Entry Detail`.`po_detail`', '`tabStock Entry Detail`.`item_code`'] + + filters = [['Stock Entry', 'docstatus', '=', 1], ['Stock Entry', 'purchase_order', '=', purchase_order]] + + supplied_item_details = {} + for row in frappe.get_all('Stock Entry', fields = fields, filters = filters): + if not row.po_detail: + continue + + key = row.po_detail + if key not in supplied_item_details: + supplied_item_details.setdefault(key, + frappe._dict({'supplied_qty': 0, 'returned_qty':0, 'total_supplied_qty':0})) + + supplied_item = supplied_item_details[key] + + if row.is_return: + supplied_item.returned_qty += row.transfer_qty + else: + supplied_item.supplied_qty += row.transfer_qty + + supplied_item.total_supplied_qty = flt(supplied_item.supplied_qty) - flt(supplied_item.returned_qty) + + return supplied_item_details \ No newline at end of file diff --git a/erpnext/tests/test_subcontracting.py b/erpnext/tests/test_subcontracting.py new file mode 100644 index 0000000000..c1a458a6dd --- /dev/null +++ b/erpnext/tests/test_subcontracting.py @@ -0,0 +1,583 @@ +from __future__ import unicode_literals +import frappe +import unittest +import copy +from frappe.utils import cint +from collections import defaultdict +from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry +from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom +from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order +from erpnext.buying.doctype.purchase_order.purchase_order import (make_rm_stock_entry, + make_purchase_receipt, get_materials_from_supplier) + +class TestSubcontracting(unittest.TestCase): + def setUp(self): + make_subcontract_items() + make_raw_materials() + make_bom_for_subcontracted_items() + + def test_po_with_bom(self): + ''' + - Set backflush based on BOM + - Create subcontracted PO for the item Subcontracted Item SA1 and add same item two times. + - Transfer the components from Stores to Supplier warehouse with batch no and serial nos. + - Create purchase receipt against the PO and check serial nos and batch no. + ''' + + set_backflush_based_on('BOM') + item_code = 'Subcontracted Item SA1' + items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 5, 'rate': 100}, + {'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 6, 'rate': 100}] + + rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 5}, + {'item_code': 'Subcontracted SRM Item 2', 'qty': 5}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 5}, + {'item_code': 'Subcontracted SRM Item 1', 'qty': 6}, + {'item_code': 'Subcontracted SRM Item 2', 'qty': 6}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 6} + ] + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + po = create_purchase_order(rm_items = items, is_subcontracted="Yes", + supplier_warehouse="_Test Warehouse 1 - _TC") + + for d in rm_items: + d['po_detail'] = po.items[0].name if d.get('qty') == 5 else po.items[1].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_receipt(po.name) + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + transferred_detais = itemwise_details.get(key) + + for field in ['qty', 'serial_no', 'batch_no']: + if value.get(field): + transfer, consumed = (transferred_detais.get(field), value.get(field)) + if field == 'serial_no': + transfer, consumed = (sorted(transfer), sorted(consumed)) + + self.assertEqual(transfer, consumed) + + def test_po_with_material_transfer(self): + ''' + - Set backflush based on Material Transfer + - Create subcontracted PO for the item Subcontracted Item SA1 and Subcontracted Item SA5. + - Transfer the components from Stores to Supplier warehouse with batch no and serial nos. + - Transfer extra item Subcontracted SRM Item 4 for the subcontract item Subcontracted Item SA5. + - Create partial purchase receipt against the PO and check serial nos and batch no. + ''' + + set_backflush_based_on('Material Transferred for Subcontract') + items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA1', 'qty': 5, 'rate': 100}, + {'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA5', 'qty': 6, 'rate': 100}] + + rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 5, 'main_item_code': 'Subcontracted Item SA1'}, + {'item_code': 'Subcontracted SRM Item 2', 'qty': 5, 'main_item_code': 'Subcontracted Item SA1'}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 5, 'main_item_code': 'Subcontracted Item SA1'}, + {'item_code': 'Subcontracted SRM Item 5', 'qty': 6, 'main_item_code': 'Subcontracted Item SA5'}, + {'item_code': 'Subcontracted SRM Item 4', 'qty': 6, 'main_item_code': 'Subcontracted Item SA5'} + ] + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + po = create_purchase_order(rm_items = items, is_subcontracted="Yes", + supplier_warehouse="_Test Warehouse 1 - _TC") + + for d in rm_items: + d['po_detail'] = po.items[0].name if d.get('qty') == 5 else po.items[1].name + + make_stock_transfer_entry(po_no = po.name, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_receipt(po.name) + pr1.remove(pr1.items[1]) + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + transferred_detais = itemwise_details.get(key) + + for field in ['qty', 'serial_no', 'batch_no']: + if value.get(field): + self.assertEqual(value.get(field), transferred_detais.get(field)) + + pr2 = make_purchase_receipt(po.name) + pr2.submit() + + for key, value in get_supplied_items(pr2).items(): + transferred_detais = itemwise_details.get(key) + + for field in ['qty', 'serial_no', 'batch_no']: + if value.get(field): + self.assertEqual(value.get(field), transferred_detais.get(field)) + + def test_subcontract_with_same_components_different_fg(self): + ''' + - Set backflush based on Material Transfer + - Create subcontracted PO for the item Subcontracted Item SA2 and Subcontracted Item SA3. + - Transfer the components from Stores to Supplier warehouse with serial nos. + - Transfer extra qty of components for the item Subcontracted Item SA2. + - Create partial purchase receipt against the PO and check serial nos and batch no. + ''' + + set_backflush_based_on('Material Transferred for Subcontract') + items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA2', 'qty': 5, 'rate': 100}, + {'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA3', 'qty': 6, 'rate': 100}] + + rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 6, 'main_item_code': 'Subcontracted Item SA2'}, + {'item_code': 'Subcontracted SRM Item 2', 'qty': 6, 'main_item_code': 'Subcontracted Item SA3'} + ] + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + po = create_purchase_order(rm_items = items, is_subcontracted="Yes", + supplier_warehouse="_Test Warehouse 1 - _TC") + + for d in rm_items: + d['po_detail'] = po.items[0].name if d.get('qty') == 5 else po.items[1].name + + make_stock_transfer_entry(po_no = po.name, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_receipt(po.name) + pr1.items[0].qty = 3 + pr1.remove(pr1.items[1]) + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + transferred_detais = itemwise_details.get(key) + self.assertEqual(value.qty, 4) + self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get('serial_no')[0:4])) + + pr2 = make_purchase_receipt(po.name) + pr2.items[0].qty = 2 + pr2.remove(pr2.items[1]) + pr2.submit() + + for key, value in get_supplied_items(pr2).items(): + transferred_detais = itemwise_details.get(key) + + self.assertEqual(value.qty, 2) + self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get('serial_no')[4:6])) + + pr3 = make_purchase_receipt(po.name) + pr3.submit() + for key, value in get_supplied_items(pr3).items(): + transferred_detais = itemwise_details.get(key) + + self.assertEqual(value.qty, 6) + self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get('serial_no')[6:12])) + + def test_return_non_consumed_materials(self): + ''' + - Set backflush based on Material Transfer + - Create subcontracted PO for the item Subcontracted Item SA2. + - Transfer the components from Stores to Supplier warehouse with serial nos. + - Transfer extra qty of component for the subcontracted item Subcontracted Item SA2. + - Create purchase receipt for full qty against the PO and change the qty of raw material. + - After that return the non consumed material back to the store from supplier's warehouse. + ''' + + set_backflush_based_on('Material Transferred for Subcontract') + items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA2', 'qty': 5, 'rate': 100}] + rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 6, 'main_item_code': 'Subcontracted Item SA2'}] + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + po = create_purchase_order(rm_items = items, is_subcontracted="Yes", + supplier_warehouse="_Test Warehouse 1 - _TC") + + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_receipt(po.name) + pr1.save() + pr1.supplied_items[0].consumed_qty = 5 + pr1.supplied_items[0].serial_no = '\n'.join(sorted( + itemwise_details.get('Subcontracted SRM Item 2').get('serial_no')[0:5] + )) + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + transferred_detais = itemwise_details.get(key) + self.assertEqual(value.qty, 5) + self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get('serial_no')[0:5])) + + po.load_from_db() + self.assertEqual(po.supplied_items[0].consumed_qty, 5) + doc = get_materials_from_supplier(po.name, [d.name for d in po.supplied_items]) + self.assertEqual(doc.items[0].qty, 1) + self.assertEqual(doc.items[0].s_warehouse, '_Test Warehouse 1 - _TC') + self.assertEqual(doc.items[0].t_warehouse, '_Test Warehouse - _TC') + self.assertEqual(get_serial_nos(doc.items[0].serial_no), + itemwise_details.get(doc.items[0].item_code)['serial_no'][5:6]) + + def test_item_with_batch_based_on_bom(self): + ''' + - Set backflush based on BOM + - Create subcontracted PO for the item Subcontracted Item SA4 (has batch no). + - Transfer the components from Stores to Supplier warehouse with batch no and serial nos. + - Transfer the components in multiple batches. + - Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches. + - Keep the qty as 2 for Subcontracted Item in the purchase receipt. + ''' + + set_backflush_based_on('BOM') + item_code = 'Subcontracted Item SA4' + items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}] + + rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 10}, + {'item_code': 'Subcontracted SRM Item 2', 'qty': 10}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 1} + ] + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + po = create_purchase_order(rm_items = items, is_subcontracted="Yes", + supplier_warehouse="_Test Warehouse 1 - _TC") + + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_receipt(po.name) + pr1.items[0].qty = 2 + add_second_row_in_pr(pr1) + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + self.assertEqual(value.qty, 4) + + pr1 = make_purchase_receipt(po.name) + pr1.items[0].qty = 2 + add_second_row_in_pr(pr1) + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + self.assertEqual(value.qty, 4) + + pr1 = make_purchase_receipt(po.name) + pr1.items[0].qty = 2 + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + self.assertEqual(value.qty, 2) + + def test_item_with_batch_based_on_material_transfer(self): + ''' + - Set backflush based on Material Transferred for Subcontract + - Create subcontracted PO for the item Subcontracted Item SA4 (has batch no). + - Transfer the components from Stores to Supplier warehouse with batch no and serial nos. + - Transfer the components in multiple batches with extra 2 qty for the batched item. + - Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches. + - Keep the qty as 2 for Subcontracted Item in the purchase receipt. + - In the first purchase receipt the batched raw materials will be consumed 2 extra qty. + ''' + + set_backflush_based_on('Material Transferred for Subcontract') + item_code = 'Subcontracted Item SA4' + items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}] + + rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 10}, + {'item_code': 'Subcontracted SRM Item 2', 'qty': 10}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3} + ] + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + po = create_purchase_order(rm_items = items, is_subcontracted="Yes", + supplier_warehouse="_Test Warehouse 1 - _TC") + + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_receipt(po.name) + pr1.items[0].qty = 2 + add_second_row_in_pr(pr1) + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + qty = 4 if key != 'Subcontracted SRM Item 3' else 6 + self.assertEqual(value.qty, qty) + + pr1 = make_purchase_receipt(po.name) + pr1.items[0].qty = 2 + add_second_row_in_pr(pr1) + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + self.assertEqual(value.qty, 4) + + pr1 = make_purchase_receipt(po.name) + pr1.items[0].qty = 2 + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + self.assertEqual(value.qty, 2) + + def test_partial_transfer_serial_no_components_based_on_material_transfer(self): + ''' + - Set backflush based on Material Transferred for Subcontract + - Create subcontracted PO for the item Subcontracted Item SA2. + - Transfer the partial components from Stores to Supplier warehouse with serial nos. + - Create partial purchase receipt against the PO and change the qty manually. + - Transfer the remaining components from Stores to Supplier warehouse with serial nos. + - Create purchase receipt for remaining qty against the PO and change the qty manually. + ''' + + set_backflush_based_on('Material Transferred for Subcontract') + item_code = 'Subcontracted Item SA2' + items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}] + + rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 5}] + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + po = create_purchase_order(rm_items = items, is_subcontracted="Yes", + supplier_warehouse="_Test Warehouse 1 - _TC") + + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_receipt(po.name) + pr1.items[0].qty = 5 + pr1.save() + + for key, value in get_supplied_items(pr1).items(): + details = itemwise_details.get(key) + self.assertEqual(value.qty, 3) + self.assertEqual(sorted(value.serial_no), sorted(details.serial_no[0:3])) + + pr1.load_from_db() + pr1.supplied_items[0].consumed_qty = 5 + pr1.supplied_items[0].serial_no = '\n'.join(itemwise_details[pr1.supplied_items[0].rm_item_code]['serial_no']) + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + details = itemwise_details.get(key) + self.assertEqual(value.qty, details.qty) + self.assertEqual(sorted(value.serial_no), sorted(details.serial_no)) + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_receipt(po.name) + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + details = itemwise_details.get(key) + self.assertEqual(value.qty, details.qty) + self.assertEqual(sorted(value.serial_no), sorted(details.serial_no)) + + def test_partial_transfer_batch_based_on_material_transfer(self): + ''' + - Set backflush based on Material Transferred for Subcontract + - Create subcontracted PO for the item Subcontracted Item SA6. + - Transfer the partial components from Stores to Supplier warehouse with batch. + - Create partial purchase receipt against the PO and change the qty manually. + - Transfer the remaining components from Stores to Supplier warehouse with batch. + - Create purchase receipt for remaining qty against the PO and change the qty manually. + ''' + + set_backflush_based_on('Material Transferred for Subcontract') + item_code = 'Subcontracted Item SA6' + items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}] + + rm_items = [{'item_code': 'Subcontracted SRM Item 3', 'qty': 5}] + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + po = create_purchase_order(rm_items = items, is_subcontracted="Yes", + supplier_warehouse="_Test Warehouse 1 - _TC") + + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_receipt(po.name) + pr1.items[0].qty = 5 + pr1.save() + + transferred_batch_no = '' + for key, value in get_supplied_items(pr1).items(): + details = itemwise_details.get(key) + self.assertEqual(value.qty, 3) + transferred_batch_no = details.batch_no + self.assertEqual(value.batch_no, details.batch_no) + + pr1.load_from_db() + pr1.supplied_items[0].consumed_qty = 5 + pr1.supplied_items[0].batch_no = list(transferred_batch_no.keys())[0] + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + details = itemwise_details.get(key) + self.assertEqual(value.qty, details.qty) + self.assertEqual(value.batch_no, details.batch_no) + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_receipt(po.name) + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + details = itemwise_details.get(key) + self.assertEqual(value.qty, details.qty) + self.assertEqual(value.batch_no, details.batch_no) + +def add_second_row_in_pr(pr): + item_dict = {} + for column in ['item_code', 'item_name', 'qty', 'uom', 'warehouse', 'stock_uom', + 'purchase_order', 'purchase_order_item', 'conversion_factor', 'rate']: + item_dict[column] = pr.items[0].get(column) + + pr.append('items', item_dict) + pr.set_missing_values() + +def get_supplied_items(pr_doc): + supplied_items = {} + for row in pr_doc.get('supplied_items'): + if row.rm_item_code not in supplied_items: + supplied_items.setdefault(row.rm_item_code, + frappe._dict({'qty': 0, 'serial_no': [], 'batch_no': defaultdict(float)})) + + details = supplied_items[row.rm_item_code] + update_item_details(row, details) + + return supplied_items + +def make_stock_in_entry(**args): + args = frappe._dict(args) + + items = {} + for row in args.rm_items: + row = frappe._dict(row) + + doc = make_stock_entry(target=row.warehouse or '_Test Warehouse - _TC', + item_code=row.item_code, qty=row.qty or 1, basic_rate=row.rate or 100) + + if row.item_code not in items: + items.setdefault(row.item_code, frappe._dict({'qty': 0, 'serial_no': [], 'batch_no': defaultdict(float)})) + + child_row = doc.items[0] + details = items[child_row.item_code] + update_item_details(child_row, details) + + return items + +def update_item_details(child_row, details): + details.qty += (child_row.get('qty') if child_row.doctype == 'Stock Entry Detail' + else child_row.get('consumed_qty')) + + if child_row.serial_no: + details.serial_no.extend(get_serial_nos(child_row.serial_no)) + + if child_row.batch_no: + details.batch_no[child_row.batch_no] += (child_row.get('qty') or child_row.get('consumed_qty')) + +def make_stock_transfer_entry(**args): + args = frappe._dict(args) + + items = [] + for row in args.rm_items: + row = frappe._dict(row) + + item = {'item_code': row.main_item_code or args.main_item_code, 'rm_item_code': row.item_code, + 'qty': row.qty or 1, 'item_name': row.item_code, 'rate': row.rate or 100, + 'stock_uom': row.stock_uom or 'Nos', 'warehouse': row.warehuose or '_Test Warehouse - _TC'} + + item_details = args.itemwise_details.get(row.item_code) + + if item_details and item_details.serial_no: + serial_nos = item_details.serial_no[0:cint(row.qty)] + item['serial_no'] = '\n'.join(serial_nos) + item_details.serial_no = list(set(item_details.serial_no) - set(serial_nos)) + + if item_details and item_details.batch_no: + for batch_no, batch_qty in item_details.batch_no.items(): + if batch_qty >= row.qty: + item['batch_no'] = batch_no + item_details.batch_no[batch_no] -= row.qty + break + + items.append(item) + + ste_dict = make_rm_stock_entry(args.po_no, items) + doc = frappe.get_doc(ste_dict) + doc.insert() + doc.submit() + + return doc + +def make_subcontract_items(): + sub_contracted_items = {'Subcontracted Item SA1': {}, 'Subcontracted Item SA2': {}, 'Subcontracted Item SA3': {}, + 'Subcontracted Item SA4': {'has_batch_no': 1, 'create_new_batch': 1, 'batch_number_series': 'SBAT.####'}, + 'Subcontracted Item SA5': {}, 'Subcontracted Item SA6': {}} + + for item, properties in sub_contracted_items.items(): + if not frappe.db.exists('Item', item): + properties.update({'is_stock_item': 1, 'is_sub_contracted_item': 1}) + make_item(item, properties) + +def make_raw_materials(): + raw_materials = {'Subcontracted SRM Item 1': {}, + 'Subcontracted SRM Item 2': {'has_serial_no': 1, 'serial_no_series': 'SRI.####'}, + 'Subcontracted SRM Item 3': {'has_batch_no': 1, 'create_new_batch': 1, 'batch_number_series': 'BAT.####'}, + 'Subcontracted SRM Item 4': {'has_serial_no': 1, 'serial_no_series': 'SRII.####'}, + 'Subcontracted SRM Item 5': {'has_serial_no': 1, 'serial_no_series': 'SRII.####'}} + + for item, properties in raw_materials.items(): + if not frappe.db.exists('Item', item): + properties.update({'is_stock_item': 1}) + make_item(item, properties) + +def make_bom_for_subcontracted_items(): + boms = { + 'Subcontracted Item SA1': ['Subcontracted SRM Item 1', 'Subcontracted SRM Item 2', 'Subcontracted SRM Item 3'], + 'Subcontracted Item SA2': ['Subcontracted SRM Item 2'], + 'Subcontracted Item SA3': ['Subcontracted SRM Item 2'], + 'Subcontracted Item SA4': ['Subcontracted SRM Item 1', 'Subcontracted SRM Item 2', 'Subcontracted SRM Item 3'], + 'Subcontracted Item SA5': ['Subcontracted SRM Item 5'], + 'Subcontracted Item SA6': ['Subcontracted SRM Item 3'] + } + + for item_code, raw_materials in boms.items(): + if not frappe.db.exists('BOM', {'item': item_code}): + make_bom(item=item_code, raw_materials=raw_materials, rate=100) + +def set_backflush_based_on(based_on): + frappe.db.set_value('Buying Settings', None, + 'backflush_raw_materials_of_subcontract_based_on', based_on) \ No newline at end of file From 9a2db0b5b196597802ddd79119108a1b4615c3b0 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 1 Jun 2021 11:24:01 +0530 Subject: [PATCH 165/180] fix: semgrep error --- .../subcontract_order_summary.py | 28 ++++++++----------- erpnext/controllers/subcontracting.py | 6 ++-- .../stock/doctype/stock_entry/stock_entry.py | 3 +- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py index 8b08d2a284..0b14e119ab 100644 --- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py +++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py @@ -104,26 +104,26 @@ def get_columns(): "fieldname": "item_code", "fieldtype": "Link", "options": "Item", - "width": 140 + "width": 160 }, { - "label": _("Qty"), + "label": _("Order Qty"), "fieldname": "qty", "fieldtype": "Float", - "width": 70 + "width": 90 }, { - "label": _("Received"), + "label": _("Received Qty"), "fieldname": "received_qty", "fieldtype": "Float", - "width": 80 + "width": 110 }, { "label": _("Supplied Item"), "fieldname": "rm_item_code", "fieldtype": "Link", "options": "Item", - "width": 140 + "width": 160 }, { "label": _("Required Qty"), @@ -138,21 +138,15 @@ def get_columns(): "width": 110 }, { - "label": _("Returned Qty"), - "fieldname": "returned_qty", - "fieldtype": "Float", - "width": 110 - }, - { - "label": _("Total Supplied"), - "fieldname": "total_supplied_qty", + "label": _("Consumed Qty"), + "fieldname": "consumed_qty", "fieldtype": "Float", "width": 120 }, { - "label": _("Consumed Qty"), - "fieldname": "consumed_qty", + "label": _("Returned Qty"), + "fieldname": "returned_qty", "fieldtype": "Float", "width": 110 - }, + } ] \ No newline at end of file diff --git a/erpnext/controllers/subcontracting.py b/erpnext/controllers/subcontracting.py index fe775766da..a9a38bd02d 100644 --- a/erpnext/controllers/subcontracting.py +++ b/erpnext/controllers/subcontracting.py @@ -101,9 +101,9 @@ class Subcontracting(object): self.set_alternative_item_details(row) for doctype in ['Purchase Receipt', 'Purchase Invoice']: - self.remove_consumed_materials(doctype) + self.update_consumed_materials(doctype) - def remove_consumed_materials(self, doctype, return_consumed_items=False): + def update_consumed_materials(self, doctype, return_consumed_items=False): '''Deduct the consumed materials from the available materials.''' pr_items = self.get_received_items(doctype) @@ -311,7 +311,7 @@ class Subcontracting(object): return self.get_purchase_orders() - consumed_items, pr_items = self.remove_consumed_materials(self.doctype, return_consumed_items=True) + consumed_items, pr_items = self.update_consumed_materials(self.doctype, return_consumed_items=True) itemwise_consumed_qty = defaultdict(float) for row in consumed_items: diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 213280870a..0009926f5d 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1291,7 +1291,8 @@ class StockEntry(StockController): item_dict[item]["qty"] = 0 # delete items with 0 qty - for item in item_dict.keys(): + list_of_items = item_dict.keys() + for item in list_of_items: if not item_dict[item]["qty"]: del item_dict[item] From 2fb5291785acf9b1e33f2cf8ebe6a4fa2c9ab887 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 7 Jun 2021 21:20:33 +0530 Subject: [PATCH 166/180] fix: toggle consumed qty field based on condition --- .../doctype/purchase_receipt/purchase_receipt.js | 11 +++++++++-- .../doctype/purchase_receipt/purchase_receipt.py | 5 +++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index 887b15a211..cac6bf884b 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -41,8 +41,6 @@ frappe.ui.form.on("Purchase Receipt", { } }); - frm.set_df_property('supplied_items', 'cannot_add_rows', 1); - }, onload: function(frm) { erpnext.queries.setup_queries(frm, "Warehouse", function() { @@ -77,6 +75,15 @@ frappe.ui.form.on("Purchase Receipt", { } frm.events.add_custom_buttons(frm); + frm.trigger('toggle_subcontracting_fields'); + }, + + toggle_subcontracting_fields: function(frm) { + frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty', + 'read_only', frm.doc.__onload && frm.doc.__onload.backflush_based_on === 'BOM'); + + frm.set_df_property('supplied_items', 'cannot_add_rows', 1); + frm.set_df_property('supplied_items', 'cannot_delete_rows', 1); }, add_custom_buttons: function(frm) { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index b8580f95a3..264561f376 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -102,6 +102,11 @@ class PurchaseReceipt(BuyingController): if self.get("items") and self.apply_putaway_rule and not self.get("is_return"): apply_putaway_rule(self.doctype, self.get("items"), self.company) + def onload(self): + super(PurchaseReceipt, self).onload() + self.set_onload("backflush_based_on", frappe.db.get_single_value('Buying Settings', + 'backflush_raw_materials_of_subcontract_based_on')) + def validate(self): self.validate_posting_time() super(PurchaseReceipt, self).validate() From ddb0ec261af7470ac2b07f103b24d9386b7b111e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 8 Jun 2021 10:36:39 +0530 Subject: [PATCH 167/180] fix: code cleanup and convert public method to private for subcontracting class --- .../doctype/purchase_order/purchase_order.js | 5 +- .../doctype/purchase_order/purchase_order.py | 13 ++- .../purchase_order_item_supplied.json | 6 +- .../subcontract_order_summary.py | 2 +- erpnext/controllers/subcontracting.py | 106 +++++++++--------- erpnext/public/js/controllers/transaction.js | 4 + .../stock/doctype/stock_entry/stock_entry.py | 10 +- 7 files changed, 76 insertions(+), 70 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 440cde6d9e..233a9c87e5 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -74,6 +74,7 @@ frappe.ui.form.on("Purchase Order", { frm.add_custom_button(__('Return of Components'), () => { frm.call({ method: 'erpnext.buying.doctype.purchase_order.purchase_order.get_materials_from_supplier', + freeze: true, freeze_message: __('Creating Stock Entry'), args: { purchase_order: frm.doc.name, po_details: po_details }, callback: function(r) { @@ -545,14 +546,14 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( ], primary_action: function() { var data = d.get_values(); - var content_msg = 'Reason for hold: ' + data.reason_for_hold; + let reason_for_hold = 'Reason for hold: ' + data.reason_for_hold; frappe.call({ method: "frappe.desk.form.utils.add_comment", args: { reference_doctype: me.frm.doctype, reference_name: me.frm.docname, - content: __(content_msg), + content: __(reason_for_hold), comment_email: frappe.session.user, comment_by: frappe.session.user_fullname }, diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 724f863e0f..eaa502ff7f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -14,12 +14,11 @@ from frappe.desk.notifications import clear_doctype_notifications from erpnext.buying.utils import validate_for_items, check_on_hold_or_closed_status from erpnext.stock.utils import get_bin from erpnext.accounts.party import get_party_account_currency -from six import string_types from erpnext.stock.doctype.item.item import get_item_defaults from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details -from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\ - unlink_inter_company_doc +from erpnext.accounts.doctype.sales_invoice.sales_invoice import (validate_inter_company_party, + update_linked_doc, unlink_inter_company_doc) form_grid_templates = { "items": "templates/form_grid/item_grid.html" @@ -504,7 +503,8 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions @frappe.whitelist() def make_rm_stock_entry(purchase_order, rm_items): rm_items_list = rm_items - if isinstance(rm_items, string_types): + + if isinstance(rm_items, str): rm_items_list = json.loads(rm_items) elif not rm_items: frappe.throw(_("No Items available for transfer")) @@ -588,7 +588,7 @@ def make_inter_company_sales_order(source_name, target_doc=None): @frappe.whitelist() def get_materials_from_supplier(purchase_order, po_details): - if isinstance(po_details, string_types): + if isinstance(po_details, str): po_details = json.loads(po_details) doc = frappe.get_cached_doc('Purchase Order', purchase_order) @@ -615,7 +615,8 @@ def make_return_stock_entry_for_subcontract(available_materials, po_doc, po_deta if value.batch_no: for batch_no, qty in value.batch_no.items(): - add_items_in_ste(ste_doc, value, value.qty, po_details, batch_no) + if qty > 0: + add_items_in_ste(ste_doc, value, value.qty, po_details, batch_no) else: add_items_in_ste(ste_doc, value, value.qty, po_details) diff --git a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json index 505ecd84c5..60247bd90b 100644 --- a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json +++ b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json @@ -22,9 +22,9 @@ "required_qty", "supplied_qty", "col_break1", + "consumed_qty", "returned_qty", - "total_supplied_qty", - "consumed_qty" + "total_supplied_qty" ], "fields": [ { @@ -183,7 +183,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2021-06-01 00:41:54.123436", + "modified": "2021-06-09 15:17:58.128242", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item Supplied", diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py index 0b14e119ab..0c0d4f0531 100644 --- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py +++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py @@ -87,7 +87,7 @@ def get_subcontracted_data(po_details, data): def get_columns(): return [ { - "label": _("Id"), + "label": _("Purchase Order"), "fieldname": "po_id", "fieldtype": "Link", "options": "Purchase Order", diff --git a/erpnext/controllers/subcontracting.py b/erpnext/controllers/subcontracting.py index a9a38bd02d..e81c0f5732 100644 --- a/erpnext/controllers/subcontracting.py +++ b/erpnext/controllers/subcontracting.py @@ -1,39 +1,37 @@ -from __future__ import unicode_literals - import frappe from frappe import _ from frappe.utils import flt, cint from collections import defaultdict from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos -class Subcontracting(object): +class Subcontracting(): def set_materials_for_subcontracted_items(self, raw_material_table): if self.doctype == 'Purchase Invoice' and not self.update_stock: return self.raw_material_table = raw_material_table - self.identify_change_in_item_table() - self.prepare_supplied_items() - self.validate_consumed_qty() + self.__identify_change_in_item_table() + self.__prepare_supplied_items() + self.__validate_consumed_qty() - def prepare_supplied_items(self): + def __prepare_supplied_items(self): self.initialized_fields() - self.get_purchase_orders() - self.get_pending_qty_to_receive() + self.__get_purchase_orders() + self.__get_pending_qty_to_receive() self.get_available_materials() - self.remove_changed_rows() - self.set_supplied_items() + self.__remove_changed_rows() + self.__set_supplied_items() def initialized_fields(self): self.available_materials = frappe._dict() self.alternative_item_details = frappe._dict() - self.get_backflush_based_on() + self.__get_backflush_based_on() - def get_backflush_based_on(self): + def __get_backflush_based_on(self): self.backflush_based_on = frappe.db.get_single_value("Buying Settings", "backflush_raw_materials_of_subcontract_based_on") - def get_purchase_orders(self): + def __get_purchase_orders(self): self.purchase_orders = [] if self.doctype == 'Purchase Order': @@ -41,14 +39,14 @@ class Subcontracting(object): self.purchase_orders = [d.purchase_order for d in self.items if d.purchase_order] - def identify_change_in_item_table(self): + def __identify_change_in_item_table(self): self.changed_name = [] if self.doctype == 'Purchase Order' or not self.get(self.raw_material_table): self.set(self.raw_material_table, []) return - item_dict = self.get_data_before_save() + item_dict = self.__get_data_before_save() if not item_dict: return True @@ -61,7 +59,7 @@ class Subcontracting(object): self.changed_name.extend(item_dict.keys()) - def get_data_before_save(self): + def __get_data_before_save(self): item_dict = {} if self.doctype == 'Purchase Receipt' and self._doc_before_save: for row in self._doc_before_save.get('items'): @@ -80,7 +78,7 @@ class Subcontracting(object): if not self.purchase_orders: return - for row in self.get_transferred_items(): + for row in self.__get_transferred_items(): key = (row.rm_item_code, row.main_item_code, row.purchase_order) if key not in self.available_materials: @@ -98,20 +96,20 @@ class Subcontracting(object): if row.batch_no: details.batch_no[row.batch_no] += row.qty - self.set_alternative_item_details(row) + self.__set_alternative_item_details(row) for doctype in ['Purchase Receipt', 'Purchase Invoice']: - self.update_consumed_materials(doctype) + self.__update_consumed_materials(doctype) - def update_consumed_materials(self, doctype, return_consumed_items=False): + def __update_consumed_materials(self, doctype, return_consumed_items=False): '''Deduct the consumed materials from the available materials.''' - pr_items = self.get_received_items(doctype) + pr_items = self.__get_received_items(doctype) if not pr_items: return ([], {}) if return_consumed_items else None pr_items = {d.name: d.get(self.get('po_field') or 'purchase_order') for d in pr_items} - consumed_materials = self.get_consumed_items(doctype, pr_items.keys()) + consumed_materials = self.__get_consumed_items(doctype, pr_items.keys()) if return_consumed_items: return (consumed_materials, pr_items) @@ -130,7 +128,7 @@ class Subcontracting(object): if row.batch_no: self.available_materials[key]['batch_no'][row.batch_no] -= row.consumed_qty - def get_transferred_items(self): + def __get_transferred_items(self): fields = ['`tabStock Entry`.`purchase_order`'] alias_dict = {'item_code': 'rm_item_code', 'subcontracted_item': 'main_item_code', 'basic_rate': 'rate'} @@ -149,7 +147,7 @@ class Subcontracting(object): return frappe.get_all('Stock Entry', fields = fields, filters=filters) - def get_received_items(self, doctype): + def __get_received_items(self, doctype): fields = [] self.po_field = 'purchase_order' if doctype == 'Purchase Receipt' else 'po_detail' @@ -162,16 +160,16 @@ class Subcontracting(object): return frappe.get_all(f'{doctype}', fields = fields, filters = filters) - def get_consumed_items(self, doctype, pr_items): + def __get_consumed_items(self, doctype, pr_items): return frappe.get_all(f'{doctype} Item Supplied', fields = ['serial_no', 'rm_item_code', 'reference_name', 'batch_no', 'consumed_qty', 'main_item_code'], filters = {'docstatus': 1, 'reference_name': ('in', list(pr_items))}) - def set_alternative_item_details(self, row): + def __set_alternative_item_details(self, row): if row.get('original_item'): self.alternative_item_details[row.get('original_item')] = row - def get_pending_qty_to_receive(self): + def __get_pending_qty_to_receive(self): '''Get qty to be received against the purchase order.''' self.qty_to_be_received = defaultdict(float) @@ -183,7 +181,7 @@ class Subcontracting(object): self.qty_to_be_received[(row.item_code, row.parent)] += row.qty - def get_materials_from_bom(self, item_code, bom_no, exploded_item=0): + def __get_materials_from_bom(self, item_code, bom_no, exploded_item=0): doctype = 'BOM Item' if not exploded_item else 'BOM Explosion Item' fields = [f'`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit'] @@ -197,7 +195,7 @@ class Subcontracting(object): return frappe.get_all('BOM', fields = fields, filters=filters, order_by = f'`tab{doctype}`.`idx`') or [] - def remove_changed_rows(self): + def __remove_changed_rows(self): if not self.changed_name: return @@ -212,7 +210,7 @@ class Subcontracting(object): i += 1 - def set_supplied_items(self): + def __set_supplied_items(self): self.bom_items = {} has_supplied_items = True if self.get(self.raw_material_table) else False @@ -222,28 +220,28 @@ class Subcontracting(object): continue if self.doctype == 'Purchase Order' or self.backflush_based_on == 'BOM': - for bom_item in self.get_materials_from_bom(row.item_code, row.bom, row.get('include_exploded_items')): + for bom_item in self.__get_materials_from_bom(row.item_code, row.bom, row.get('include_exploded_items')): qty = (flt(bom_item.qty_consumed_per_unit) * flt(row.qty) * row.conversion_factor) bom_item.main_item_code = row.item_code - self.update_reserve_warehouse(bom_item, row) - self.set_alternative_item(bom_item) - self.add_supplied_item(row, bom_item, qty) + self.__update_reserve_warehouse(bom_item, row) + self.__set_alternative_item(bom_item) + self.__add_supplied_item(row, bom_item, qty) elif self.backflush_based_on != 'BOM': for key, transfer_item in self.available_materials.items(): if (key[1], key[2]) == (row.item_code, row.purchase_order) and transfer_item.qty > 0: - qty = self.get_qty_based_on_material_transfer(row, transfer_item) or 0 + qty = self.__get_qty_based_on_material_transfer(row, transfer_item) or 0 transfer_item.qty -= qty - self.add_supplied_item(row, transfer_item.get('item_details'), qty) + self.__add_supplied_item(row, transfer_item.get('item_details'), qty) if self.qty_to_be_received: self.qty_to_be_received[(row.item_code, row.purchase_order)] -= row.qty - def update_reserve_warehouse(self, row, item): + def __update_reserve_warehouse(self, row, item): if self.doctype == 'Purchase Order': row.reserve_warehouse = (self.set_reserve_warehouse or item.warehouse) - def get_qty_based_on_material_transfer(self, item_row, transfer_item): + def __get_qty_based_on_material_transfer(self, item_row, transfer_item): key = (item_row.item_code, item_row.purchase_order) if self.qty_to_be_received == item_row.qty: @@ -257,11 +255,11 @@ class Subcontracting(object): return qty - def set_alternative_item(self, bom_item): + def __set_alternative_item(self, bom_item): if self.alternative_item_details.get(bom_item.rm_item_code): bom_item.update(self.alternative_item_details[bom_item.rm_item_code]) - def add_supplied_item(self, item_row, bom_item, qty): + def __add_supplied_item(self, item_row, bom_item, qty): bom_item.conversion_factor = item_row.conversion_factor rm_obj = self.append(self.raw_material_table, bom_item) rm_obj.reference_name = item_row.name @@ -269,15 +267,15 @@ class Subcontracting(object): if self.doctype == 'Purchase Order': rm_obj.required_qty = qty else: - self.set_batch_nos(bom_item, item_row, rm_obj, qty) + self.__set_batch_nos(bom_item, item_row, rm_obj, qty) - def set_batch_nos(self, bom_item, item_row, rm_obj, qty): + def __set_batch_nos(self, bom_item, item_row, rm_obj, qty): key = (rm_obj.rm_item_code, item_row.item_code, item_row.purchase_order) if (self.available_materials.get(key) and self.available_materials[key]['batch_no']): for batch_no, batch_qty in self.available_materials[key]['batch_no'].items(): if batch_qty >= qty: - self.set_batch_no_as_per_qty(item_row, rm_obj, batch_no, qty) + self.__set_batch_no_as_per_qty(item_row, rm_obj, batch_no, qty) self.available_materials[key]['batch_no'][batch_no] -= qty return @@ -285,18 +283,18 @@ class Subcontracting(object): qty -= batch_qty new_rm_obj = self.append(self.raw_material_table, bom_item) new_rm_obj.reference_name = item_row.name - self.set_batch_no_as_per_qty(item_row, new_rm_obj, batch_no, batch_qty) + self.__set_batch_no_as_per_qty(item_row, new_rm_obj, batch_no, batch_qty) self.available_materials[key]['batch_no'][batch_no] = 0 else: rm_obj.required_qty = qty rm_obj.consumed_qty = qty - self.set_serial_nos(item_row, rm_obj) + self.__set_serial_nos(item_row, rm_obj) - def set_batch_no_as_per_qty(self, item_row, rm_obj, batch_no, qty): + def __set_batch_no_as_per_qty(self, item_row, rm_obj, batch_no, qty): rm_obj.update({'consumed_qty': qty, 'batch_no': batch_no, 'required_qty': qty}) - self.set_serial_nos(item_row, rm_obj) + self.__set_serial_nos(item_row, rm_obj) - def set_serial_nos(self, item_row, rm_obj): + def __set_serial_nos(self, item_row, rm_obj): key = (rm_obj.rm_item_code, item_row.item_code, item_row.purchase_order) if (self.available_materials.get(key) and self.available_materials[key]['serial_no']): used_serial_nos = self.available_materials[key]['serial_no'][0: cint(rm_obj.consumed_qty)] @@ -310,17 +308,17 @@ class Subcontracting(object): if self.is_subcontracted != 'Yes': return - self.get_purchase_orders() - consumed_items, pr_items = self.update_consumed_materials(self.doctype, return_consumed_items=True) + self.__get_purchase_orders() + consumed_items, pr_items = self.__update_consumed_materials(self.doctype, return_consumed_items=True) itemwise_consumed_qty = defaultdict(float) for row in consumed_items: key = (row.rm_item_code, row.main_item_code, pr_items.get(row.reference_name)) itemwise_consumed_qty[key] += row.consumed_qty - self.update_consumed_qty_in_po(itemwise_consumed_qty) + self.__update_consumed_qty_in_po(itemwise_consumed_qty) - def update_consumed_qty_in_po(self, itemwise_consumed_qty): + def __update_consumed_qty_in_po(self, itemwise_consumed_qty): fields = ['main_item_code', 'rm_item_code', 'parent', 'supplied_qty', 'name'] filters = {'docstatus': 1, 'parent': ('in', self.purchase_orders)} @@ -334,7 +332,7 @@ class Subcontracting(object): itemwise_consumed_qty[key] -= consumed_qty frappe.db.set_value('Purchase Order Item Supplied', row.name, 'consumed_qty', consumed_qty) - def validate_consumed_qty(self): + def __validate_consumed_qty(self): for row in self.get(self.raw_material_table): if flt(row.consumed_qty) == 0.0 and row.get('serial_no'): msg = f'Row {row.idx}: the consumed qty cannot be zero for the item {frappe.bold(row.rm_item_code)}' diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 89fed3bf0d..978c8f4879 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -723,6 +723,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ var me = this; var item = frappe.get_doc(cdt, cdn); + if (item && item.doctype === 'Purchase Receipt Item Supplied') { + return; + } + if (item && item.serial_no) { if (!item.item_code) { this.frm.trigger("item_code", cdt, cdn); diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 0009926f5d..66f8b63cb9 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1005,10 +1005,12 @@ class StockEntry(StockController): if self.purchase_order and self.purpose == "Send to Subcontractor": #Get PO Supplied Items Details item_wh = frappe._dict(frappe.db.sql(""" - select rm_item_code, reserve_warehouse - from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup - where po.name = poitemsup.parent - and po.name = %s""",self.purchase_order)) + SELECT + rm_item_code, reserve_warehouse + FROM + `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup + WHERE + po.name = poitemsup.parent and po.name = %s """,self.purchase_order)) for item in itervalues(item_dict): if self.pro_doc and cint(self.pro_doc.from_wip_warehouse): From 5cc3f14506bb75fd525eb774cc5d70a5f4204947 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 15 Jun 2021 17:29:52 +0530 Subject: [PATCH 168/180] fix: purchase invoice qty change not recalculate the consumed qty and added test cases for purchase invoice --- .../purchase_invoice/purchase_invoice.json | 617 +++++------------- .../purchase_invoice/purchase_invoice.py | 2 + erpnext/controllers/buying_controller.py | 5 + erpnext/controllers/subcontracting.py | 41 +- erpnext/public/js/controllers/buying.js | 11 + .../purchase_receipt/purchase_receipt.js | 9 - .../purchase_receipt/purchase_receipt.py | 5 - erpnext/tests/test_subcontracting.py | 267 +++++++- 8 files changed, 461 insertions(+), 496 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index a714ac7827..00ef7d5c18 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -175,9 +175,7 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "naming_series", @@ -189,9 +187,7 @@ "options": "ACC-PINV-.YYYY.-\nACC-PINV-RET-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1, - "show_days": 1, - "show_seconds": 1 + "set_only_once": 1 }, { "fieldname": "supplier", @@ -203,9 +199,7 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "bold": 1, @@ -217,9 +211,7 @@ "label": "Supplier Name", "oldfieldname": "supplier_name", "oldfieldtype": "Data", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fetch_from": "supplier.tax_id", @@ -227,27 +219,21 @@ "fieldtype": "Read Only", "label": "Tax Id", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "due_date", "fieldtype": "Date", "label": "Due Date", "oldfieldname": "due_date", - "oldfieldtype": "Date", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Date" }, { "default": "0", "fieldname": "is_paid", "fieldtype": "Check", "label": "Is Paid", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "default": "0", @@ -255,25 +241,19 @@ "fieldtype": "Check", "label": "Is Return (Debit Note)", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "default": "0", "fieldname": "apply_tds", "fieldtype": "Check", "label": "Apply Tax Withholding Amount", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -283,17 +263,13 @@ "label": "Company", "options": "Company", "print_hide": 1, - "remember_last_selected_value": 1, - "show_days": 1, - "show_seconds": 1 + "remember_last_selected_value": 1 }, { "fieldname": "cost_center", "fieldtype": "Link", "label": "Cost Center", - "options": "Cost Center", - "show_days": 1, - "show_seconds": 1 + "options": "Cost Center" }, { "default": "Today", @@ -305,9 +281,7 @@ "oldfieldtype": "Date", "print_hide": 1, "reqd": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "fieldname": "posting_time", @@ -316,8 +290,6 @@ "no_copy": 1, "print_hide": 1, "print_width": "100px", - "show_days": 1, - "show_seconds": 1, "width": "100px" }, { @@ -326,9 +298,7 @@ "fieldname": "set_posting_time", "fieldtype": "Check", "label": "Edit Posting Date and Time", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "amended_from", @@ -340,58 +310,44 @@ "oldfieldtype": "Link", "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:doc.on_hold", "fieldname": "sb_14", "fieldtype": "Section Break", - "label": "Hold Invoice", - "show_days": 1, - "show_seconds": 1 + "label": "Hold Invoice" }, { "default": "0", "fieldname": "on_hold", "fieldtype": "Check", - "label": "Hold Invoice", - "show_days": 1, - "show_seconds": 1 + "label": "Hold Invoice" }, { "depends_on": "eval:doc.on_hold", "description": "Once set, this invoice will be on hold till the set date", "fieldname": "release_date", "fieldtype": "Date", - "label": "Release Date", - "show_days": 1, - "show_seconds": 1 + "label": "Release Date" }, { "fieldname": "cb_17", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "depends_on": "eval:doc.on_hold", "fieldname": "hold_comment", "fieldtype": "Small Text", - "label": "Reason For Putting On Hold", - "show_days": 1, - "show_seconds": 1 + "label": "Reason For Putting On Hold" }, { "collapsible": 1, "collapsible_depends_on": "bill_no", "fieldname": "supplier_invoice_details", "fieldtype": "Section Break", - "label": "Supplier Invoice Details", - "show_days": 1, - "show_seconds": 1 + "label": "Supplier Invoice Details" }, { "fieldname": "bill_no", @@ -399,15 +355,11 @@ "label": "Supplier Invoice No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_15", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "bill_date", @@ -416,17 +368,13 @@ "no_copy": 1, "oldfieldname": "bill_date", "oldfieldtype": "Date", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "depends_on": "return_against", "fieldname": "returns", "fieldtype": "Section Break", - "label": "Returns", - "show_days": 1, - "show_seconds": 1 + "label": "Returns" }, { "depends_on": "return_against", @@ -436,34 +384,26 @@ "no_copy": 1, "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact", - "show_days": 1, - "show_seconds": 1 + "label": "Address and Contact" }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "contact_person", @@ -471,67 +411,51 @@ "in_global_search": 1, "label": "Contact Person", "options": "Contact", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "label": "Contact", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "contact_email", "fieldtype": "Small Text", "label": "Contact Email", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-tag" }, { "fieldname": "currency", @@ -540,9 +464,7 @@ "oldfieldname": "currency", "oldfieldtype": "Select", "options": "Currency", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "conversion_rate", @@ -551,24 +473,18 @@ "oldfieldname": "conversion_rate", "oldfieldtype": "Currency", "precision": "9", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break2", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "price_list_currency", @@ -576,18 +492,14 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "default": "0", @@ -596,15 +508,11 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "depends_on": "update_stock", @@ -613,9 +521,7 @@ "fieldtype": "Link", "label": "Set Accepted Warehouse", "options": "Warehouse", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "depends_on": "update_stock", @@ -625,15 +531,11 @@ "label": "Rejected Warehouse", "no_copy": 1, "options": "Warehouse", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "default": "No", @@ -641,33 +543,25 @@ "fieldtype": "Select", "label": "Raw Materials Supplied", "options": "No\nYes", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-shopping-cart" }, { "default": "0", "fieldname": "update_stock", "fieldtype": "Check", "label": "Update Stock", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode", - "show_days": 1, - "show_seconds": 1 + "label": "Scan Barcode" }, { "allow_bulk_edit": 1, @@ -677,56 +571,43 @@ "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Purchase Invoice Item", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "pricing_rule_details", "fieldtype": "Section Break", - "label": "Pricing Rules", - "show_days": 1, - "show_seconds": 1 + "label": "Pricing Rules" }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_materials_supplied", "fieldtype": "Section Break", - "label": "Raw Materials Supplied", - "show_days": 1, - "show_seconds": 1 + "label": "Raw Materials Supplied" }, { + "depends_on": "update_stock", "fieldname": "supplied_items", "fieldtype": "Table", "label": "Supplied Items", - "options": "Purchase Receipt Item Supplied", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "no_copy": 1, + "options": "Purchase Receipt Item Supplied" }, { "fieldname": "section_break_26", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_total", @@ -734,9 +615,7 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_net_total", @@ -746,24 +625,18 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_28", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "net_total", @@ -773,56 +646,42 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-money" }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_49", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "section_break_51", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "fieldname": "taxes_and_charges", @@ -831,9 +690,7 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "taxes", @@ -841,17 +698,13 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges", - "show_days": 1, - "show_seconds": 1 + "options": "Purchase Taxes and Charges" }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup", - "show_days": 1, - "show_seconds": 1 + "label": "Tax Breakup" }, { "fieldname": "other_charges_calculation", @@ -860,17 +713,13 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-money" }, { "fieldname": "base_taxes_and_charges_added", @@ -880,9 +729,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -892,9 +739,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -904,15 +749,11 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_40", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "taxes_and_charges_added", @@ -922,9 +763,7 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -934,9 +773,7 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "total_taxes_and_charges", @@ -944,18 +781,14 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "section_break_44", "fieldtype": "Section Break", - "label": "Additional Discount", - "show_days": 1, - "show_seconds": 1 + "label": "Additional Discount" }, { "default": "Grand Total", @@ -963,9 +796,7 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "base_discount_amount", @@ -973,38 +804,28 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_46", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "section_break_49", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "fieldname": "base_grand_total", @@ -1014,9 +835,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -1026,9 +845,7 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -1038,9 +855,7 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_in_words", @@ -1050,17 +865,13 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break8", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 1, - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -1071,9 +882,7 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -1083,9 +892,7 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -1095,9 +902,7 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "in_words", @@ -1107,9 +912,7 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "total_advance", @@ -1120,9 +923,7 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "outstanding_amount", @@ -1133,18 +934,14 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "default": "0", "depends_on": "grand_total", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total", - "show_days": 1, - "show_seconds": 1 + "label": "Disable Rounded Total" }, { "collapsible": 1, @@ -1152,26 +949,20 @@ "depends_on": "eval:doc.is_paid===1||(doc.advances && doc.advances.length>0)", "fieldname": "payments_section", "fieldtype": "Section Break", - "label": "Payments", - "show_days": 1, - "show_seconds": 1 + "label": "Payments" }, { "fieldname": "mode_of_payment", "fieldtype": "Link", "label": "Mode of Payment", "options": "Mode of Payment", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "cash_bank_account", "fieldtype": "Link", "label": "Cash/Bank Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "clearance_date", @@ -1179,15 +970,11 @@ "label": "Clearance Date", "no_copy": 1, "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "col_br_payments", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "depends_on": "is_paid", @@ -1196,9 +983,7 @@ "label": "Paid Amount", "no_copy": 1, "options": "currency", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "base_paid_amount", @@ -1207,9 +992,7 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, @@ -1217,9 +1000,7 @@ "depends_on": "grand_total", "fieldname": "write_off", "fieldtype": "Section Break", - "label": "Write Off", - "show_days": 1, - "show_seconds": 1 + "label": "Write Off" }, { "fieldname": "write_off_amount", @@ -1227,9 +1008,7 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "base_write_off_amount", @@ -1238,15 +1017,11 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_61", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1254,9 +1029,7 @@ "fieldtype": "Link", "label": "Write Off Account", "options": "Account", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1264,9 +1037,7 @@ "fieldtype": "Link", "label": "Write Off Cost Center", "options": "Cost Center", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -1276,17 +1047,13 @@ "label": "Advance Payments", "oldfieldtype": "Section Break", "options": "fa fa-money", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "default": "0", "fieldname": "allocate_advances_automatically", "fieldtype": "Check", - "label": "Set Advances and Allocate (FIFO)", - "show_days": 1, - "show_seconds": 1 + "label": "Set Advances and Allocate (FIFO)" }, { "depends_on": "eval:!doc.allocate_advances_automatically", @@ -1294,9 +1061,7 @@ "fieldtype": "Button", "label": "Get Advances Paid", "oldfieldtype": "Button", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "advances", @@ -1306,26 +1071,20 @@ "oldfieldname": "advance_allocation_details", "oldfieldtype": "Table", "options": "Purchase Invoice Advance", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:(!doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms", - "show_days": 1, - "show_seconds": 1 + "label": "Payment Terms" }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template", - "show_days": 1, - "show_seconds": 1 + "options": "Payment Terms Template" }, { "fieldname": "payment_schedule", @@ -1333,9 +1092,7 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -1343,33 +1100,25 @@ "fieldname": "terms_section_break", "fieldtype": "Section Break", "label": "Terms and Conditions", - "options": "fa fa-legal", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-legal" }, { "fieldname": "tc_name", "fieldtype": "Link", "label": "Terms", "options": "Terms and Conditions", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", - "label": "Terms and Conditions1", - "show_days": 1, - "show_seconds": 1 + "label": "Terms and Conditions1" }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings", - "show_days": 1, - "show_seconds": 1 + "label": "Printing Settings" }, { "allow_on_submit": 1, @@ -1377,9 +1126,7 @@ "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "allow_on_submit": 1, @@ -1387,15 +1134,11 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_112", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "allow_on_submit": 1, @@ -1407,18 +1150,14 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1, - "show_days": 1, - "show_seconds": 1 + "report_hide": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, @@ -1427,9 +1166,7 @@ "label": "More Information", "oldfieldtype": "Section Break", "options": "fa fa-file-text", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "credit_to", @@ -1440,9 +1177,7 @@ "options": "Account", "print_hide": 1, "reqd": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "fieldname": "party_account_currency", @@ -1452,9 +1187,7 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "default": "No", @@ -1464,9 +1197,7 @@ "oldfieldname": "is_opening", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "against_expense_account", @@ -1476,15 +1207,11 @@ "no_copy": 1, "oldfieldname": "against_expense_account", "oldfieldtype": "Small Text", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_63", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "default": "Draft", @@ -1493,9 +1220,7 @@ "in_standard_filter": 1, "label": "Status", "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled\nInternal Transfer", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "inter_company_invoice_reference", @@ -1504,9 +1229,7 @@ "no_copy": 1, "options": "Sales Invoice", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "remarks", @@ -1515,18 +1238,14 @@ "no_copy": 1, "oldfieldname": "remarks", "oldfieldtype": "Text", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", "label": "Subscription Section", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "allow_on_submit": 1, @@ -1535,9 +1254,7 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "allow_on_submit": 1, @@ -1546,15 +1263,11 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_114", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "auto_repeat", @@ -1563,32 +1276,24 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference", - "show_days": 1, - "show_seconds": 1 + "label": "Update Auto Repeat Reference" }, { "collapsible": 1, "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", - "label": "Accounting Dimensions ", - "show_days": 1, - "show_seconds": 1 + "label": "Accounting Dimensions " }, { "fieldname": "dimension_col_break", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "default": "0", @@ -1596,9 +1301,7 @@ "fieldname": "is_internal_supplier", "fieldtype": "Check", "label": "Is Internal Supplier", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "tax_withholding_category", @@ -1606,25 +1309,19 @@ "hidden": 1, "label": "Tax Withholding Category", "options": "Tax Withholding Category", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "billing_address", "fieldtype": "Link", "label": "Select Billing Address", - "options": "Address", - "show_days": 1, - "show_seconds": 1 + "options": "Address" }, { "fieldname": "billing_address_display", "fieldtype": "Small Text", "label": "Billing Address", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "project", @@ -1638,9 +1335,7 @@ "fieldname": "unrealized_profit_loss_account", "fieldtype": "Link", "label": "Unrealized Profit / Loss Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "depends_on": "eval:doc.is_internal_supplier", @@ -1649,9 +1344,7 @@ "fieldname": "represents_company", "fieldtype": "Link", "label": "Represents Company", - "options": "Company", - "show_days": 1, - "show_seconds": 1 + "options": "Company" }, { "depends_on": "eval:doc.update_stock && doc.is_internal_supplier", @@ -1663,8 +1356,6 @@ "options": "Warehouse", "print_hide": 1, "print_width": "50px", - "show_days": 1, - "show_seconds": 1, "width": "50px" }, { @@ -1692,7 +1383,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2021-06-09 12:30:25.632109", + "modified": "2021-06-15 18:20:56.806195", "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 0ee0bc7e11..45d89ad1c8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -400,6 +400,7 @@ class PurchaseInvoice(BuyingController): # because updating ordered qty in bin depends upon updated ordered qty in PO if self.update_stock == 1: self.update_stock_ledger() + self.set_consumed_qty_in_po() from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit update_serial_nos_after_submit(self, "items") @@ -998,6 +999,7 @@ class PurchaseInvoice(BuyingController): if self.update_stock == 1: self.update_stock_ledger() self.delete_auto_created_batches() + self.set_consumed_qty_in_po() self.make_gl_entries_on_cancel() diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 1907885717..0b0da5f413 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -58,6 +58,11 @@ class BuyingController(StockController, Subcontracting): if self.doctype in ("Purchase Receipt", "Purchase Invoice"): self.update_valuation_rate() + def onload(self): + super(BuyingController, self).onload() + self.set_onload("backflush_based_on", frappe.db.get_single_value('Buying Settings', + 'backflush_raw_materials_of_subcontract_based_on')) + def set_missing_values(self, for_validate=False): super(BuyingController, self).set_missing_values(for_validate) diff --git a/erpnext/controllers/subcontracting.py b/erpnext/controllers/subcontracting.py index e81c0f5732..db841626a5 100644 --- a/erpnext/controllers/subcontracting.py +++ b/erpnext/controllers/subcontracting.py @@ -40,9 +40,10 @@ class Subcontracting(): self.purchase_orders = [d.purchase_order for d in self.items if d.purchase_order] def __identify_change_in_item_table(self): - self.changed_name = [] + self.__changed_name = [] + self.__reference_name = [] - if self.doctype == 'Purchase Order' or not self.get(self.raw_material_table): + if self.doctype == 'Purchase Order' or self.is_new(): self.set(self.raw_material_table, []) return @@ -51,17 +52,18 @@ class Subcontracting(): return True for n_row in self.items: + self.__reference_name.append(n_row.name) if (n_row.name not in item_dict) or (n_row.item_code, n_row.qty) != item_dict[n_row.name]: - self.changed_name.append(n_row.name) + self.__changed_name.append(n_row.name) if item_dict.get(n_row.name): del item_dict[n_row.name] - self.changed_name.extend(item_dict.keys()) + self.__changed_name.extend(item_dict.keys()) def __get_data_before_save(self): item_dict = {} - if self.doctype == 'Purchase Receipt' and self._doc_before_save: + if self.doctype in ['Purchase Receipt', 'Purchase Invoice'] and self._doc_before_save: for row in self._doc_before_save.get('items'): item_dict[row.name] = (row.item_code, row.qty) @@ -149,7 +151,7 @@ class Subcontracting(): def __get_received_items(self, doctype): fields = [] - self.po_field = 'purchase_order' if doctype == 'Purchase Receipt' else 'po_detail' + self.po_field = 'purchase_order' for field in ['name', self.po_field, 'parent']: fields.append(f'`tab{doctype} Item`.`{field}`') @@ -161,9 +163,9 @@ class Subcontracting(): return frappe.get_all(f'{doctype}', fields = fields, filters = filters) def __get_consumed_items(self, doctype, pr_items): - return frappe.get_all(f'{doctype} Item Supplied', + return frappe.get_all('Purchase Receipt Item Supplied', fields = ['serial_no', 'rm_item_code', 'reference_name', 'batch_no', 'consumed_qty', 'main_item_code'], - filters = {'docstatus': 1, 'reference_name': ('in', list(pr_items))}) + filters = {'docstatus': 1, 'reference_name': ('in', list(pr_items)), 'parenttype': doctype}) def __set_alternative_item_details(self, row): if row.get('original_item'): @@ -196,13 +198,16 @@ class Subcontracting(): return frappe.get_all('BOM', fields = fields, filters=filters, order_by = f'`tab{doctype}`.`idx`') or [] def __remove_changed_rows(self): - if not self.changed_name: + if not self.__changed_name: return i=1 self.set(self.raw_material_table, []) for d in self._doc_before_save.supplied_items: - if d.reference_name in self.changed_name: + if d.reference_name in self.__changed_name: + continue + + if (d.reference_name not in self.__reference_name): continue d.idx = i @@ -215,8 +220,8 @@ class Subcontracting(): has_supplied_items = True if self.get(self.raw_material_table) else False for row in self.items: - if (self.doctype != 'Purchase Order' and ((self.changed_name and row.name not in self.changed_name) - or (has_supplied_items and not self.changed_name))): + if (self.doctype != 'Purchase Order' and ((self.__changed_name and row.name not in self.__changed_name) + or (has_supplied_items and not self.__changed_name))): continue if self.doctype == 'Purchase Order' or self.backflush_based_on == 'BOM': @@ -305,16 +310,18 @@ class Subcontracting(): self.available_materials[key]['serial_no'].remove(sn) def set_consumed_qty_in_po(self): + # Update consumed qty back in the purchase order if self.is_subcontracted != 'Yes': return self.__get_purchase_orders() - consumed_items, pr_items = self.__update_consumed_materials(self.doctype, return_consumed_items=True) - itemwise_consumed_qty = defaultdict(float) - for row in consumed_items: - key = (row.rm_item_code, row.main_item_code, pr_items.get(row.reference_name)) - itemwise_consumed_qty[key] += row.consumed_qty + for doctype in ['Purchase Receipt', 'Purchase Invoice']: + consumed_items, pr_items = self.__update_consumed_materials(doctype, return_consumed_items=True) + + for row in consumed_items: + key = (row.rm_item_code, row.main_item_code, pr_items.get(row.reference_name)) + itemwise_consumed_qty[key] += row.consumed_qty self.__update_consumed_qty_in_po(itemwise_consumed_qty) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index e7dcd41068..5c9f5d7da4 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -122,9 +122,20 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ this.set_from_product_bundle(); } + this.toggle_subcontracting_fields(); this._super(); }, + toggle_subcontracting_fields: function() { + if (in_list(['Purchase Receipt', 'Purchase Invoice'], this.frm.doc.doctype)) { + this.frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty', + 'read_only', this.frm.doc.__onload && this.frm.doc.__onload.backflush_based_on === 'BOM'); + + this.frm.set_df_property('supplied_items', 'cannot_add_rows', 1); + this.frm.set_df_property('supplied_items', 'cannot_delete_rows', 1); + } + }, + supplier: function() { var me = this; erpnext.utils.get_party_details(this.frm, null, null, function(){ diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index cac6bf884b..befdad9692 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -75,15 +75,6 @@ frappe.ui.form.on("Purchase Receipt", { } frm.events.add_custom_buttons(frm); - frm.trigger('toggle_subcontracting_fields'); - }, - - toggle_subcontracting_fields: function(frm) { - frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty', - 'read_only', frm.doc.__onload && frm.doc.__onload.backflush_based_on === 'BOM'); - - frm.set_df_property('supplied_items', 'cannot_add_rows', 1); - frm.set_df_property('supplied_items', 'cannot_delete_rows', 1); }, add_custom_buttons: function(frm) { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 264561f376..b8580f95a3 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -102,11 +102,6 @@ class PurchaseReceipt(BuyingController): if self.get("items") and self.apply_putaway_rule and not self.get("is_return"): apply_putaway_rule(self.doctype, self.get("items"), self.company) - def onload(self): - super(PurchaseReceipt, self).onload() - self.set_onload("backflush_based_on", frappe.db.get_single_value('Buying Settings', - 'backflush_raw_materials_of_subcontract_based_on')) - def validate(self): self.validate_posting_time() super(PurchaseReceipt, self).validate() diff --git a/erpnext/tests/test_subcontracting.py b/erpnext/tests/test_subcontracting.py index c1a458a6dd..d2438f8c60 100644 --- a/erpnext/tests/test_subcontracting.py +++ b/erpnext/tests/test_subcontracting.py @@ -10,7 +10,7 @@ from erpnext.stock.doctype.item.test_item import make_item from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.buying.doctype.purchase_order.purchase_order import (make_rm_stock_entry, - make_purchase_receipt, get_materials_from_supplier) + make_purchase_receipt, make_purchase_invoice, get_materials_from_supplier) class TestSubcontracting(unittest.TestCase): def setUp(self): @@ -458,10 +458,273 @@ class TestSubcontracting(unittest.TestCase): self.assertEqual(value.qty, details.qty) self.assertEqual(value.batch_no, details.batch_no) + + def test_item_with_batch_based_on_material_transfer_for_purchase_invoice(self): + ''' + - Set backflush based on Material Transferred for Subcontract + - Create subcontracted PO for the item Subcontracted Item SA4 (has batch no). + - Transfer the components from Stores to Supplier warehouse with batch no and serial nos. + - Transfer the components in multiple batches with extra 2 qty for the batched item. + - Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches. + - Keep the qty as 2 for Subcontracted Item in the purchase receipt. + - In the first purchase receipt the batched raw materials will be consumed 2 extra qty. + ''' + + set_backflush_based_on('Material Transferred for Subcontract') + item_code = 'Subcontracted Item SA4' + items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}] + + rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 10}, + {'item_code': 'Subcontracted SRM Item 2', 'qty': 10}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3} + ] + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + po = create_purchase_order(rm_items = items, is_subcontracted="Yes", + supplier_warehouse="_Test Warehouse 1 - _TC") + + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_invoice(po.name) + pr1.update_stock = 1 + pr1.items[0].qty = 2 + pr1.items[0].expense_account = 'Stock Adjustment - _TC' + add_second_row_in_pr(pr1) + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + qty = 4 if key != 'Subcontracted SRM Item 3' else 6 + self.assertEqual(value.qty, qty) + + pr1 = make_purchase_invoice(po.name) + pr1.update_stock = 1 + pr1.items[0].expense_account = 'Stock Adjustment - _TC' + pr1.items[0].qty = 2 + add_second_row_in_pr(pr1) + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + self.assertEqual(value.qty, 4) + + pr1 = make_purchase_invoice(po.name) + pr1.update_stock = 1 + pr1.items[0].qty = 2 + pr1.items[0].expense_account = 'Stock Adjustment - _TC' + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + self.assertEqual(value.qty, 2) + + def test_partial_transfer_serial_no_components_based_on_material_transfer_for_purchase_invoice(self): + ''' + - Set backflush based on Material Transferred for Subcontract + - Create subcontracted PO for the item Subcontracted Item SA2. + - Transfer the partial components from Stores to Supplier warehouse with serial nos. + - Create partial purchase receipt against the PO and change the qty manually. + - Transfer the remaining components from Stores to Supplier warehouse with serial nos. + - Create purchase receipt for remaining qty against the PO and change the qty manually. + ''' + + set_backflush_based_on('Material Transferred for Subcontract') + item_code = 'Subcontracted Item SA2' + items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}] + + rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 5}] + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + po = create_purchase_order(rm_items = items, is_subcontracted="Yes", + supplier_warehouse="_Test Warehouse 1 - _TC") + + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_invoice(po.name) + pr1.update_stock = 1 + pr1.items[0].qty = 5 + pr1.items[0].expense_account = 'Stock Adjustment - _TC' + pr1.save() + + for key, value in get_supplied_items(pr1).items(): + details = itemwise_details.get(key) + self.assertEqual(value.qty, 3) + self.assertEqual(sorted(value.serial_no), sorted(details.serial_no[0:3])) + + pr1.load_from_db() + pr1.supplied_items[0].consumed_qty = 5 + pr1.supplied_items[0].serial_no = '\n'.join(itemwise_details[pr1.supplied_items[0].rm_item_code]['serial_no']) + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + details = itemwise_details.get(key) + self.assertEqual(value.qty, details.qty) + self.assertEqual(sorted(value.serial_no), sorted(details.serial_no)) + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_invoice(po.name) + pr1.update_stock = 1 + pr1.items[0].expense_account = 'Stock Adjustment - _TC' + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + details = itemwise_details.get(key) + self.assertEqual(value.qty, details.qty) + self.assertEqual(sorted(value.serial_no), sorted(details.serial_no)) + + def test_partial_transfer_batch_based_on_material_transfer_for_purchase_invoice(self): + ''' + - Set backflush based on Material Transferred for Subcontract + - Create subcontracted PO for the item Subcontracted Item SA6. + - Transfer the partial components from Stores to Supplier warehouse with batch. + - Create partial purchase receipt against the PO and change the qty manually. + - Transfer the remaining components from Stores to Supplier warehouse with batch. + - Create purchase receipt for remaining qty against the PO and change the qty manually. + ''' + + set_backflush_based_on('Material Transferred for Subcontract') + item_code = 'Subcontracted Item SA6' + items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}] + + rm_items = [{'item_code': 'Subcontracted SRM Item 3', 'qty': 5}] + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + po = create_purchase_order(rm_items = items, is_subcontracted="Yes", + supplier_warehouse="_Test Warehouse 1 - _TC") + + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_invoice(po.name) + pr1.update_stock = 1 + pr1.items[0].qty = 5 + pr1.items[0].expense_account = 'Stock Adjustment - _TC' + pr1.save() + + transferred_batch_no = '' + for key, value in get_supplied_items(pr1).items(): + details = itemwise_details.get(key) + self.assertEqual(value.qty, 3) + transferred_batch_no = details.batch_no + self.assertEqual(value.batch_no, details.batch_no) + + pr1.load_from_db() + pr1.supplied_items[0].consumed_qty = 5 + pr1.supplied_items[0].batch_no = list(transferred_batch_no.keys())[0] + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + details = itemwise_details.get(key) + self.assertEqual(value.qty, details.qty) + self.assertEqual(value.batch_no, details.batch_no) + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_invoice(po.name) + pr1.update_stock = 1 + pr1.items[0].expense_account = 'Stock Adjustment - _TC' + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + details = itemwise_details.get(key) + self.assertEqual(value.qty, details.qty) + self.assertEqual(value.batch_no, details.batch_no) + + def test_item_with_batch_based_on_bom_for_purchase_invoice(self): + ''' + - Set backflush based on BOM + - Create subcontracted PO for the item Subcontracted Item SA4 (has batch no). + - Transfer the components from Stores to Supplier warehouse with batch no and serial nos. + - Transfer the components in multiple batches. + - Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches. + - Keep the qty as 2 for Subcontracted Item in the purchase receipt. + ''' + + set_backflush_based_on('BOM') + item_code = 'Subcontracted Item SA4' + items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}] + + rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 10}, + {'item_code': 'Subcontracted SRM Item 2', 'qty': 10}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 3}, + {'item_code': 'Subcontracted SRM Item 3', 'qty': 1} + ] + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + po = create_purchase_order(rm_items = items, is_subcontracted="Yes", + supplier_warehouse="_Test Warehouse 1 - _TC") + + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_invoice(po.name) + pr1.update_stock = 1 + pr1.items[0].qty = 2 + pr1.items[0].expense_account = 'Stock Adjustment - _TC' + add_second_row_in_pr(pr1) + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + self.assertEqual(value.qty, 4) + + pr1 = make_purchase_invoice(po.name) + pr1.update_stock = 1 + pr1.items[0].qty = 2 + pr1.items[0].expense_account = 'Stock Adjustment - _TC' + add_second_row_in_pr(pr1) + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + self.assertEqual(value.qty, 4) + + pr1 = make_purchase_invoice(po.name) + pr1.update_stock = 1 + pr1.items[0].qty = 2 + pr1.items[0].expense_account = 'Stock Adjustment - _TC' + pr1.save() + pr1.submit() + + for key, value in get_supplied_items(pr1).items(): + self.assertEqual(value.qty, 2) + def add_second_row_in_pr(pr): item_dict = {} for column in ['item_code', 'item_name', 'qty', 'uom', 'warehouse', 'stock_uom', - 'purchase_order', 'purchase_order_item', 'conversion_factor', 'rate']: + 'purchase_order', 'purchase_order_item', 'conversion_factor', 'rate', 'expense_account', 'po_detail']: item_dict[column] = pr.items[0].get(column) pr.append('items', item_dict) From f5db407461c1ee898a84d83587cf6f3eae3791c1 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 18 Jun 2021 20:37:42 +0530 Subject: [PATCH 169/180] fix: available qty for consumption --- .../purchase_order/test_purchase_order.py | 3 - .../purchase_receipt_item_supplied.json | 20 ++++-- erpnext/controllers/buying_controller.py | 10 +-- erpnext/controllers/subcontracting.py | 66 ++++++++++++++++--- erpnext/stock/stock_ledger.py | 2 +- erpnext/tests/test_subcontracting.py | 31 +++++++++ 6 files changed, 110 insertions(+), 22 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 33d1971451..8563b97ab7 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -847,9 +847,6 @@ class TestPurchaseOrder(unittest.TestCase): for item in rm_items: transferred_rm_map[item.get('rm_item_code')] = item - for item in pr.get('supplied_items'): - self.assertEqual(item.get('required_qty'), (transferred_rm_map[item.get('rm_item_code')].get('qty') / order_qty) * received_qty) - update_backflush_based_on("BOM") def test_supplied_qty_against_subcontracted_po(self): diff --git a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json index d8c37f5881..f9cd72015a 100644 --- a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json +++ b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json @@ -26,7 +26,8 @@ "secbreak_3", "batch_no", "col_break4", - "serial_no" + "serial_no", + "purchase_order" ], "fields": [ { @@ -81,9 +82,10 @@ "fieldname": "required_qty", "fieldtype": "Float", "in_list_view": 1, - "label": "Required Qty", + "label": "Available Qty For Consumption", "oldfieldname": "required_qty", "oldfieldtype": "Currency", + "print_hide": 1, "read_only": 1 }, { @@ -91,7 +93,7 @@ "fieldname": "consumed_qty", "fieldtype": "Float", "in_list_view": 1, - "label": "Consumed Qty", + "label": "Qty to Be Consumed", "oldfieldname": "consumed_qty", "oldfieldtype": "Currency", "reqd": 1 @@ -190,12 +192,22 @@ "fieldtype": "Data", "label": "Item Name", "read_only": 1 + }, + { + "fieldname": "purchase_order", + "fieldtype": "Link", + "hidden": 1, + "label": "Purchase Order", + "no_copy": 1, + "options": "Purchase Order", + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-05-29 17:22:14.977117", + "modified": "2021-06-19 19:33:04.431213", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Receipt Item Supplied", diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 0b0da5f413..6a550e0e97 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -292,11 +292,13 @@ class BuyingController(StockController, Subcontracting): if item in self.sub_contracted_items and not item.bom: frappe.throw(_("Please select BOM in BOM field for Item {0}").format(item.item_code)) - if self.doctype == "Purchase Order": - for supplied_item in self.get("supplied_items"): - if not supplied_item.reserve_warehouse: - frappe.throw(_("Reserved Warehouse is mandatory for Item {0} in Raw Materials supplied").format(frappe.bold(supplied_item.rm_item_code))) + if self.doctype != "Purchase Order": + return + for row in self.get("supplied_items"): + if not row.reserve_warehouse: + msg = f"Reserved Warehouse is mandatory for the Item {frappe.bold(row.rm_item_code)} in Raw Materials supplied" + frappe.throw(_(msg)) else: for item in self.get("items"): if item.bom: diff --git a/erpnext/controllers/subcontracting.py b/erpnext/controllers/subcontracting.py index db841626a5..36ae110216 100644 --- a/erpnext/controllers/subcontracting.py +++ b/erpnext/controllers/subcontracting.py @@ -1,6 +1,7 @@ import frappe +import copy from frappe import _ -from frappe.utils import flt, cint +from frappe.utils import flt, cint, get_link_to_form from collections import defaultdict from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -12,7 +13,7 @@ class Subcontracting(): self.raw_material_table = raw_material_table self.__identify_change_in_item_table() self.__prepare_supplied_items() - self.__validate_consumed_qty() + self.__validate_supplied_items() def __prepare_supplied_items(self): self.initialized_fields() @@ -24,6 +25,7 @@ class Subcontracting(): def initialized_fields(self): self.available_materials = frappe._dict() + self.__transferred_items = frappe._dict() self.alternative_item_details = frappe._dict() self.__get_backflush_based_on() @@ -100,6 +102,7 @@ class Subcontracting(): self.__set_alternative_item_details(row) + self.__transferred_items = copy.deepcopy(self.available_materials) for doctype in ['Purchase Receipt', 'Purchase Invoice']: self.__update_consumed_materials(doctype) @@ -254,6 +257,8 @@ class Subcontracting(): if self.qty_to_be_received: qty = (flt(item_row.qty) * flt(transfer_item.qty)) / flt(self.qty_to_be_received.get(key, 0)) + transfer_item.item_details.required_qty = transfer_item.qty + if (transfer_item.serial_no or frappe.get_cached_value('UOM', transfer_item.item_details.stock_uom, 'must_be_whole_number')): return frappe.utils.ceil(qty) @@ -272,12 +277,15 @@ class Subcontracting(): if self.doctype == 'Purchase Order': rm_obj.required_qty = qty else: + rm_obj.consumed_qty = 0 + rm_obj.purchase_order = item_row.purchase_order self.__set_batch_nos(bom_item, item_row, rm_obj, qty) def __set_batch_nos(self, bom_item, item_row, rm_obj, qty): key = (rm_obj.rm_item_code, item_row.item_code, item_row.purchase_order) if (self.available_materials.get(key) and self.available_materials[key]['batch_no']): + new_rm_obj = None for batch_no, batch_qty in self.available_materials[key]['batch_no'].items(): if batch_qty >= qty: self.__set_batch_no_as_per_qty(item_row, rm_obj, batch_no, qty) @@ -290,13 +298,21 @@ class Subcontracting(): new_rm_obj.reference_name = item_row.name self.__set_batch_no_as_per_qty(item_row, new_rm_obj, batch_no, batch_qty) self.available_materials[key]['batch_no'][batch_no] = 0 + + if abs(qty) > 0 and not new_rm_obj: + self.__set_consumed_qty(rm_obj, qty) else: - rm_obj.required_qty = qty - rm_obj.consumed_qty = qty + self.__set_consumed_qty(rm_obj, qty, bom_item.required_qty or qty) self.__set_serial_nos(item_row, rm_obj) + def __set_consumed_qty(self, rm_obj, consumed_qty, required_qty=0): + rm_obj.required_qty = required_qty + rm_obj.consumed_qty = consumed_qty + def __set_batch_no_as_per_qty(self, item_row, rm_obj, batch_no, qty): - rm_obj.update({'consumed_qty': qty, 'batch_no': batch_no, 'required_qty': qty}) + rm_obj.update({'consumed_qty': qty, 'batch_no': batch_no, + 'required_qty': qty, 'purchase_order': item_row.purchase_order}) + self.__set_serial_nos(item_row, rm_obj) def __set_serial_nos(self, item_row, rm_obj): @@ -339,9 +355,39 @@ class Subcontracting(): itemwise_consumed_qty[key] -= consumed_qty frappe.db.set_value('Purchase Order Item Supplied', row.name, 'consumed_qty', consumed_qty) - def __validate_consumed_qty(self): - for row in self.get(self.raw_material_table): - if flt(row.consumed_qty) == 0.0 and row.get('serial_no'): - msg = f'Row {row.idx}: the consumed qty cannot be zero for the item {frappe.bold(row.rm_item_code)}' + def __validate_supplied_items(self): + if self.doctype not in ['Purchase Invoice', 'Purchase Receipt']: + return - frappe.throw(_(msg),title=_('Consumed Items Qty Check')) \ No newline at end of file + for row in self.get(self.raw_material_table): + self.__validate_consumed_qty(row) + + key = (row.rm_item_code, row.main_item_code, row.purchase_order) + if not self.__transferred_items or not self.__transferred_items.get(key): + return + + self.__validate_batch_no(row, key) + self.__validate_serial_no(row, key) + + def __validate_consumed_qty(self, row): + if self.backflush_based_on != 'BOM' and flt(row.consumed_qty) == 0.0: + msg = f'Row {row.idx}: the consumed qty cannot be zero for the item {frappe.bold(row.rm_item_code)}' + + frappe.throw(_(msg),title=_('Consumed Items Qty Check')) + + def __validate_batch_no(self, row, key): + if row.get('batch_no') and row.get('batch_no') not in self.__transferred_items.get(key).get('batch_no'): + link = get_link_to_form('Purchase Order', row.purchase_order) + msg = f'The Batch No {frappe.bold(row.get("batch_no"))} has not supplied against the Purchase Order {link}' + frappe.throw(_(msg), title=_("Incorrect Batch Consumed")) + + def __validate_serial_no(self, row, key): + if row.get('serial_no'): + serial_nos = get_serial_nos(row.get('serial_no')) + incorrect_sn = set(serial_nos).difference(self.__transferred_items.get(key).get('serial_no')) + + if incorrect_sn: + incorrect_sn = "\n".join(incorrect_sn) + link = get_link_to_form('Purchase Order', row.purchase_order) + msg = f'The Serial Nos {incorrect_sn} has not supplied against the Purchase Order {link}' + frappe.throw(_(msg), title=_("Incorrect Serial Number Consumed")) \ No newline at end of file diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index fb2ecab249..9fe89c3fa5 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -485,7 +485,7 @@ class update_entries_after(object): # Recalculate subcontracted item's rate in case of subcontracted purchase receipt/invoice if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_subcontracted") == 'Yes': - doc = frappe.get_cached_doc(sle.voucher_type, sle.voucher_no) + doc = frappe.get_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() diff --git a/erpnext/tests/test_subcontracting.py b/erpnext/tests/test_subcontracting.py index d2438f8c60..8b0ce0957d 100644 --- a/erpnext/tests/test_subcontracting.py +++ b/erpnext/tests/test_subcontracting.py @@ -395,6 +395,37 @@ class TestSubcontracting(unittest.TestCase): self.assertEqual(value.qty, details.qty) self.assertEqual(sorted(value.serial_no), sorted(details.serial_no)) + def test_incorrect_serial_no_components_based_on_material_transfer(self): + ''' + - Set backflush based on Material Transferred for Subcontract + - Create subcontracted PO for the item Subcontracted Item SA2. + - Transfer the serialized componenets to the supplier. + - Create purchase receipt and change the serial no which is not transferred. + - System should throw the error and not allowed to save the purchase receipt. + ''' + + set_backflush_based_on('Material Transferred for Subcontract') + item_code = 'Subcontracted Item SA2' + items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}] + + rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 10}] + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + po = create_purchase_order(rm_items = items, is_subcontracted="Yes", + supplier_warehouse="_Test Warehouse 1 - _TC") + + for d in rm_items: + d['po_detail'] = po.items[0].name + + make_stock_transfer_entry(po_no = po.name, main_item_code=item_code, + rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)) + + pr1 = make_purchase_receipt(po.name) + pr1.save() + pr1.supplied_items[0].serial_no = 'ABCD' + self.assertRaises(frappe.ValidationError, pr1.save) + pr1.delete() + def test_partial_transfer_batch_based_on_material_transfer(self): ''' - Set backflush based on Material Transferred for Subcontract From 3d7f660bec36faf4c73dab15c7e6974e4473591f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 20 Jun 2021 10:20:35 +0530 Subject: [PATCH 170/180] fix: test case for Project Profitability report --- .../project_profitability/test_project_profitability.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/report/project_profitability/test_project_profitability.py b/erpnext/projects/report/project_profitability/test_project_profitability.py index ea6bdb54ca..180926fe25 100644 --- a/erpnext/projects/report/project_profitability/test_project_profitability.py +++ b/erpnext/projects/report/project_profitability/test_project_profitability.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import unittest import frappe -from frappe.utils import getdate, nowdate +from frappe.utils import getdate, nowdate, add_days from erpnext.hr.doctype.employee.test_employee import make_employee from erpnext.projects.doctype.timesheet.test_timesheet import make_salary_structure_for_timesheet, make_timesheet from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice @@ -16,17 +16,22 @@ class TestProjectProfitability(unittest.TestCase): make_salary_structure_for_timesheet(emp, company='_Test Company') self.timesheet = make_timesheet(emp, simulate = True, is_billable=1) self.salary_slip = make_salary_slip(self.timesheet.name) + holidays = self.salary_slip.get_holidays_for_employee(nowdate(), nowdate()) + if holidays: + frappe.db.set_value('Payroll Settings', None, 'include_holidays_in_total_working_days', 1) + self.salary_slip.submit() self.sales_invoice = make_sales_invoice(self.timesheet.name, '_Test Item', '_Test Customer') self.sales_invoice.due_date = nowdate() self.sales_invoice.submit() frappe.db.set_value('HR Settings', None, 'standard_working_hours', 8) + frappe.db.set_value('Payroll Settings', None, 'include_holidays_in_total_working_days', 0) def test_project_profitability(self): filters = { 'company': '_Test Company', - 'start_date': getdate(), + 'start_date': add_days(getdate(), -3), 'end_date': getdate() } From 582f18772632d98ab138390532aa52a836aa9ef3 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 21 Jun 2021 00:59:02 +0530 Subject: [PATCH 171/180] fix: rate not able to change in purchase order --- erpnext/controllers/taxes_and_totals.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 2bb83ea7f0..56da5b71da 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -658,7 +658,13 @@ class calculate_taxes_and_totals(object): item.margin_type = None item.margin_rate_or_amount = 0.0 - if item.margin_type and item.margin_rate_or_amount: + if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate): + item.margin_type = "Amount" + item.margin_rate_or_amount = flt(item.rate - item.price_list_rate, + item.precision("margin_rate_or_amount")) + item.rate_with_margin = item.rate + + elif item.margin_type and item.margin_rate_or_amount: margin_value = item.margin_rate_or_amount if item.margin_type == 'Amount' else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100 rate_with_margin = flt(item.price_list_rate) + flt(margin_value) base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate) From fb89008a13a49e57825228bd8a179c9e8e963aa2 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 21 Jun 2021 10:49:09 +0530 Subject: [PATCH 172/180] fix(pos): unsupported operand type -=: for 'float' and 'NoneType' (#26097) --- .../accounts/doctype/accounts_settings/accounts_settings.json | 4 ++-- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- erpnext/public/js/controllers/transaction.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 0ff7230e55..703e93c075 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -260,7 +260,7 @@ "description": "If enabled, ledger entries will be posted for change amount in POS transactions", "fieldname": "post_change_gl_entries", "fieldtype": "Check", - "label": "Change Ledger Entries for Change Amount" + "label": "Create Ledger Entries for Change Amount" } ], "icon": "icon-cog", @@ -268,7 +268,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-06-16 13:14:45.739107", + "modified": "2021-06-17 20:26:03.721202", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index e14f305fc5..55a5b99907 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -989,7 +989,7 @@ class SalesInvoice(SellingController): for payment_mode in self.payments: if skip_change_gl_entries and payment_mode.account == self.account_for_change_amount: - payment_mode.base_amount -= self.change_amount + payment_mode.base_amount -= flt(self.change_amount) if payment_mode.amount: # POS, make payment entries diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 978c8f4879..6dc40f05e7 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -387,7 +387,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if(this.frm.doc.scan_barcode) { frappe.call({ - method: "erpnext.selling.page.point_of_sale.point_of_sale.search_serial_or_batch_or_barcode_number", + method: "erpnext.selling.page.point_of_sale.point_of_sale.search_for_serial_or_batch_or_barcode_number", args: { search_value: this.frm.doc.scan_barcode } }).then(r => { const data = r && r.message; From 4b32ccb1245ff8256b776cc992da64b5e37cd737 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 21 Jun 2021 10:49:25 +0530 Subject: [PATCH 173/180] fix(pos): unsupported operand type -=: for 'float' and 'NoneType' (#26097) From e78364c1917e0eeccd45587221fc51f00e185586 Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 21 Jun 2021 11:15:16 +0530 Subject: [PATCH 174/180] fix: status indicator for delivery notes (#26062) On list view `per_returned` isn't fetched i.e. `undefined` which become 0 hence the list view indicator is false. This "computation" is already done by status updater, so relying on doc.status is better than redefining it. --- erpnext/stock/doctype/delivery_note/delivery_note_list.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_list.js b/erpnext/stock/doctype/delivery_note/delivery_note_list.js index f08125b199..0402898047 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note_list.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note_list.js @@ -6,8 +6,8 @@ frappe.listview_settings['Delivery Note'] = { return [__("Return"), "gray", "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 (doc.status === "Return Issued") { + return [__("Return Issued"), "grey", "status,=,Return Issued"]; } else if (flt(doc.per_billed, 2) < 100) { return [__("To Bill"), "orange", "per_billed,<,100"]; } else if (flt(doc.per_billed, 2) === 100) { From 773aabae440a8d85d772ea0cd8f9749faee02dfc Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 21 Jun 2021 14:42:40 +0530 Subject: [PATCH 175/180] fix: allow to select group warehouse while downloading materials from production plan --- .../production_plan/production_plan.js | 21 ++++++- .../production_plan/production_plan.py | 59 ++++++++++--------- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 64d584118f..056f600c3b 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -306,8 +306,25 @@ frappe.ui.form.on('Production Plan', { }, download_materials_required: function(frm) { - let get_template_url = 'erpnext.manufacturing.doctype.production_plan.production_plan.download_raw_materials'; - open_url_post(frappe.request.url, { cmd: get_template_url, doc: frm.doc }); + const fields = [{ + fieldname: 'warehouses', + fieldtype: 'Table MultiSelect', + label: __('Warehouses'), + default: frm.doc.from_warehouse, + options: "Production Plan Material Request Warehouse", + get_query: function () { + return { + filters: { + company: frm.doc.company + } + }; + }, + }]; + + frappe.prompt(fields, (row) => { + let get_template_url = 'erpnext.manufacturing.doctype.production_plan.production_plan.download_raw_materials'; + open_url_post(frappe.request.url, { cmd: get_template_url, doc: frm.doc, warehouses:row.warehouses }); + }, __('Select Warehouses to get Stock for Materials Planning'), __('Get Stock')); }, show_progress: function(frm) { diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 46e047654b..0ede1bd4ab 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -98,7 +98,7 @@ class ProductionPlan(Document): def get_items(self): self.set('po_items', []) if self.get_items_from == "Sales Order": - self.get_so_items() + self.get_so_items() elif self.get_items_from == "Material Request": self.get_mr_items() @@ -170,11 +170,11 @@ class ProductionPlan(Document): refs = {} for data in items: item_details = get_item_details(data.item_code) - if self.combine_items: + if self.combine_items: if item_details.bom_no in refs: refs[item_details.bom_no]['so_details'].append({ 'sales_order': data.parent, - 'sales_order_item': data.name, + 'sales_order_item': data.name, 'qty': data.pending_qty }) refs[item_details.bom_no]['qty'] += data.pending_qty @@ -188,10 +188,10 @@ class ProductionPlan(Document): } refs[item_details.bom_no]['so_details'].append({ 'sales_order': data.parent, - 'sales_order_item': data.name, + 'sales_order_item': data.name, 'qty': data.pending_qty }) - + pi = self.append('po_items', { 'include_exploded_items': 1, 'warehouse': data.warehouse, @@ -209,12 +209,12 @@ class ProductionPlan(Document): pi.sales_order = data.parent pi.sales_order_item = data.name pi.description = data.description - + elif self.get_items_from == "Material Request": pi.material_request = data.parent pi.material_request_item = data.name pi.description = data.description - + if refs: for po_item in self.po_items: po_item.planned_qty = refs[po_item.bom_no]['qty'] @@ -477,18 +477,19 @@ class ProductionPlan(Document): msgprint(_("No material request created")) @frappe.whitelist() -def download_raw_materials(doc): +def download_raw_materials(doc, warehouses=None): if isinstance(doc, string_types): doc = frappe._dict(json.loads(doc)) item_list = [['Item Code', 'Description', 'Stock UOM', 'Warehouse', 'Required Qty as per BOM', - 'Projected Qty', 'Actual Qty', 'Ordered Qty', 'Reserved Qty for Production', - 'Safety Stock', 'Required Qty']] + 'Projected Qty', 'Available Qty In Hand', 'Ordered Qty', 'Planned Qty', + 'Reserved Qty for Production', 'Safety Stock', 'Required Qty']] - for d in get_items_for_material_requests(doc): + doc.warehouse = None + for d in get_items_for_material_requests(doc, warehouses=warehouses, get_parent_warehouse_data=True): item_list.append([d.get('item_code'), d.get('description'), d.get('stock_uom'), d.get('warehouse'), d.get('required_bom_qty'), d.get('projected_qty'), d.get('actual_qty'), d.get('ordered_qty'), - d.get('reserved_qty_for_production'), d.get('safety_stock'), d.get('quantity')]) + d.get('planned_qty'), d.get('reserved_qty_for_production'), d.get('safety_stock'), d.get('quantity')]) if not doc.get('for_warehouse'): row = {'item_code': d.get('item_code')} @@ -507,7 +508,7 @@ def get_exploded_items(item_details, company, bom_no, include_non_stock_items, p ifnull(sum(bei.stock_qty/ifnull(bom.quantity, 1)), 0)*%s as qty, item.item_name, bei.description, bei.stock_uom, item.min_order_qty, bei.source_warehouse, item.default_material_request_type, item.min_order_qty, item_default.default_warehouse, - item.purchase_uom, item_uom.conversion_factor + item.purchase_uom, item_uom.conversion_factor, item.safety_stock from `tabBOM Explosion Item` bei JOIN `tabBOM` bom ON bom.name = bei.parent @@ -677,32 +678,36 @@ def get_bin_details(row, company, for_warehouse=None, all_warehouse=False): return frappe.db.sql(""" select ifnull(sum(projected_qty),0) as projected_qty, ifnull(sum(actual_qty),0) as actual_qty, ifnull(sum(ordered_qty),0) as ordered_qty, - ifnull(sum(reserved_qty_for_production),0) as reserved_qty_for_production, warehouse from `tabBin` - where item_code = %(item_code)s {conditions} + ifnull(sum(reserved_qty_for_production),0) as reserved_qty_for_production, warehouse, + ifnull(sum(planned_qty),0) as planned_qty + from `tabBin` where item_code = %(item_code)s {conditions} group by item_code, warehouse """.format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1) +def get_warehouse_list(warehouses, warehouse_list=[]): + if isinstance(warehouses, string_types): + warehouses = json.loads(warehouses) + + for row in warehouses: + child_warehouses = frappe.db.get_descendants('Warehouse', row.get("warehouse")) + if child_warehouses: + warehouse_list.extend(child_warehouses) + else: + warehouse_list.append(row.get("warehouse")) + @frappe.whitelist() -def get_items_for_material_requests(doc, warehouses=None): +def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_data=None): if isinstance(doc, string_types): doc = frappe._dict(json.loads(doc)) warehouse_list = [] if warehouses: - if isinstance(warehouses, string_types): - warehouses = json.loads(warehouses) - - for row in warehouses: - child_warehouses = frappe.db.get_descendants('Warehouse', row.get("warehouse")) - if child_warehouses: - warehouse_list.extend(child_warehouses) - else: - warehouse_list.append(row.get("warehouse")) + get_warehouse_list(warehouses, warehouse_list) if warehouse_list: warehouses = list(set(warehouse_list)) - if doc.get("for_warehouse") and doc.get("for_warehouse") in warehouses: + if doc.get("for_warehouse") and not get_parent_warehouse_data and doc.get("for_warehouse") in warehouses: warehouses.remove(doc.get("for_warehouse")) warehouse_list = None @@ -795,7 +800,7 @@ def get_items_for_material_requests(doc, warehouses=None): if items: mr_items.append(items) - if not ignore_existing_ordered_qty and warehouses: + if (not ignore_existing_ordered_qty or get_parent_warehouse_data) and warehouses: new_mr_items = [] for item in mr_items: get_materials_from_other_locations(item, warehouses, new_mr_items, company) From 8347eb1dbae8591cb42fd4a2398c7fad0cdcf6fb Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 21 Jun 2021 15:38:44 +0530 Subject: [PATCH 176/180] Update production_plan.js --- .../manufacturing/doctype/production_plan/production_plan.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 056f600c3b..450aa04a73 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -323,7 +323,7 @@ frappe.ui.form.on('Production Plan', { frappe.prompt(fields, (row) => { let get_template_url = 'erpnext.manufacturing.doctype.production_plan.production_plan.download_raw_materials'; - open_url_post(frappe.request.url, { cmd: get_template_url, doc: frm.doc, warehouses:row.warehouses }); + open_url_post(frappe.request.url, { cmd: get_template_url, doc: frm.doc, warehouses: row.warehouses }); }, __('Select Warehouses to get Stock for Materials Planning'), __('Get Stock')); }, From f97206b3cbb9aeee730d67db8161f75138404595 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 21 Jun 2021 19:38:37 +0530 Subject: [PATCH 177/180] fix: Sort website products by weightage mentioned in Item master --- erpnext/shopping_cart/product_query.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/shopping_cart/product_query.py b/erpnext/shopping_cart/product_query.py index 36d446ed0f..dd94c26bc6 100644 --- a/erpnext/shopping_cart/product_query.py +++ b/erpnext/shopping_cart/product_query.py @@ -61,7 +61,8 @@ class ProductQuery: ], or_filters=self.or_filters, start=start, - limit=self.page_length + limit=self.page_length, + order_by="weightage desc" ) items_dict = {item.name: item for item in items} @@ -71,7 +72,15 @@ 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, start=start, 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, + order_by="weightage desc" + ) for item in result: product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get('product_info') From 889140fd8c5c8684f394a36516878a2490efe21c Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Tue, 22 Jun 2021 16:26:19 +0530 Subject: [PATCH 178/180] fix: sql syntax error in get_project_name method (#26147) --- erpnext/controllers/queries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 7bd739a6ad..4ada834425 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -315,7 +315,7 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select {fields} from `tabProject` where `tabProject`.status not in ("Completed", "Cancelled") - and {cond} {match_cond} {scond} + and {cond} {scond} {match_cond} order by if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), idx desc, From 219a87d53072cab01a79009f1195a359ce059a4a Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Tue, 22 Jun 2021 16:28:58 +0530 Subject: [PATCH 179/180] fix: disable sales order cancellation if linked to draft invoice (#26125) --- erpnext/selling/doctype/sales_order/sales_order.py | 2 +- .../selling/doctype/sales_order/test_sales_order.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 551f715bd5..41f57a34d3 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -233,7 +233,7 @@ class SalesOrder(SellingController): # Checks Sales Invoice submit_rv = frappe.db.sql_list("""select t1.name from `tabSales Invoice` t1,`tabSales Invoice Item` t2 - where t1.name = t2.parent and t2.sales_order = %s and t1.docstatus = 1""", + where t1.name = t2.parent and t2.sales_order = %s and t1.docstatus < 2""", self.name) if submit_rv: diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 987371066a..974648d6d4 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1217,6 +1217,19 @@ class TestSalesOrder(unittest.TestCase): # To test if the SO does NOT have a Blanket Order self.assertEqual(so_doc.items[0].blanket_order, None) + def test_so_cancellation_when_si_drafted(self): + """ + Test to check if Sales Order gets cancelled if Sales Invoice is in Draft state + Expected result: sales order should not get cancelled + """ + so = make_sales_order() + so.submit() + si = make_sales_invoice(so.name) + si.save() + + self.assertRaises(frappe.ValidationError, so.cancel) + + def make_sales_order(**args): so = frappe.new_doc("Sales Order") From c05496a5a7c52e7f7c4a8a4f5c55f71b52d810a8 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Tue, 22 Jun 2021 17:53:53 +0530 Subject: [PATCH 180/180] fix: fixed rounding off ordered percent to 100 in condition (#26152) --- erpnext/stock/doctype/material_request/material_request.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 92c8d21387..6e66f9869c 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -101,7 +101,8 @@ frappe.ui.form.on('Material Request', { } if (frm.doc.docstatus == 1 && frm.doc.status != 'Stopped') { - if (flt(frm.doc.per_ordered, 2) < 100) { + let precision = frappe.defaults.get_default("float_precision"); + if (flt(frm.doc.per_ordered, precision) < 100) { let add_create_pick_list_button = () => { frm.add_custom_button(__('Pick List'), () => frm.events.create_pick_list(frm), __('Create'));