From 6c748966e732387383399389176afeb2fcc1ab7d Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Mon, 6 Sep 2021 17:27:47 +0500 Subject: [PATCH 0001/1047] feat: Asset Capitalization Form --- .../doctype/asset_capitalization/__init__.py | 0 .../asset_capitalization.js | 389 +++++++++++++++++ .../asset_capitalization.json | 340 +++++++++++++++ .../asset_capitalization.py | 402 ++++++++++++++++++ .../test_asset_capitalization.py | 8 + .../__init__.py | 0 .../asset_capitalization_asset_item.json | 85 ++++ .../asset_capitalization_asset_item.py | 8 + .../__init__.py | 0 .../asset_capitalization_service_item.json | 129 ++++++ .../asset_capitalization_service_item.py | 8 + .../__init__.py | 0 .../asset_capitalization_stock_item.json | 137 ++++++ .../asset_capitalization_stock_item.py | 8 + erpnext/assets/workspace/assets/assets.json | 13 +- 15 files changed, 1526 insertions(+), 1 deletion(-) create mode 100644 erpnext/assets/doctype/asset_capitalization/__init__.py create mode 100644 erpnext/assets/doctype/asset_capitalization/asset_capitalization.js create mode 100644 erpnext/assets/doctype/asset_capitalization/asset_capitalization.json create mode 100644 erpnext/assets/doctype/asset_capitalization/asset_capitalization.py create mode 100644 erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py create mode 100644 erpnext/assets/doctype/asset_capitalization_asset_item/__init__.py create mode 100644 erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json create mode 100644 erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py create mode 100644 erpnext/assets/doctype/asset_capitalization_service_item/__init__.py create mode 100644 erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json create mode 100644 erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py create mode 100644 erpnext/assets/doctype/asset_capitalization_stock_item/__init__.py create mode 100644 erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json create mode 100644 erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py diff --git a/erpnext/assets/doctype/asset_capitalization/__init__.py b/erpnext/assets/doctype/asset_capitalization/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js new file mode 100644 index 0000000000..9276d00c05 --- /dev/null +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -0,0 +1,389 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.provide("erpnext.assets"); + + +erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.stock.StockController { + setup() { + this.setup_posting_date_time_check(); + } + + onload() { + this.setup_queries(); + } + + refresh() { + erpnext.hide_company(); + } + + setup_queries() { + var me = this; + + me.setup_warehouse_query(); + + me.frm.set_query("target_item_code", function() { + return erpnext.queries.item(); + }); + + me.frm.set_query("target_asset", function() { + var filters = {}; + + if (me.frm.doc.target_item_code) { + filters['item_code'] = me.frm.doc.target_item_code; + } + + filters['status'] = ["not in", ["Draft", "Scrapped", "Sold"]] + filters['docstatus'] = 1; + + return { + filters: filters + } + }); + + me.frm.set_query("asset", "asset_items", function() { + var filters = { + 'status': ["not in", ["Draft", "Scrapped", "Sold"]], + 'docstatus': 1 + } + + if (me.frm.doc.target_asset) { + filters['name'] = ['!=', me.frm.doc.target_asset] + } + + return { + filters: filters + } + }); + + me.frm.set_query("item_code", "stock_items", function() { + return erpnext.queries.item({"is_stock_item": 1}); + }); + + me.frm.set_query("item_code", "service_items", function() { + return erpnext.queries.item({"is_stock_item": 0, "is_fixed_asset": 0}); + }); + + me.frm.set_query('batch_no', 'stock_items', function(doc, cdt, cdn) { + var item = locals[cdt][cdn]; + if(!item.item_code) { + frappe.throw(__("Please enter Item Code to get Batch Number")); + } else { + var filters = { + 'item_code': item.item_code, + 'posting_date': me.frm.doc.posting_date || frappe.datetime.nowdate(), + 'warehouse': item.warehouse + } + + return { + query : "erpnext.controllers.queries.get_batch_no", + filters: filters + } + } + }); + + me.frm.set_query('expense_account', 'service_items', function() { + return { + filters: { + "account_type": ['in', ["Tax", "Expense Account", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"]], + "is_group": 0, + "company": me.frm.doc.company + } + }; + }); + } + + target_item_code() { + return this.get_target_item_details(); + } + + target_asset() { + return this.get_target_asset_details(); + } + + item_code(doc, cdt, cdn) { + var row = frappe.get_doc(cdt, cdn); + if (cdt === "Asset Capitalization Stock Item") { + this.get_consumed_stock_item_details(row); + } else if (cdt == "Asset Capitalization Service Item") { + this.get_service_item_details(row); + } + } + + warehouse(doc, cdt, cdn) { + var row = frappe.get_doc(cdt, cdn); + if (cdt === "Asset Capitalization Stock Item") { + this.get_warehouse_details(row); + } + } + + asset(doc, cdt, cdn) { + var row = frappe.get_doc(cdt, cdn); + if (cdt === "Asset Capitalization Asset Item") { + this.get_consumed_asset_details(row); + } + } + + posting_date() { + if (this.frm.doc.posting_date) { + this.get_all_item_warehouse_details(); + } + } + + posting_time() { + if (this.frm.doc.posting_time) { + this.get_all_item_warehouse_details(); + } + } + + finance_book() { + this.get_all_asset_values(); + } + + stock_qty() { + this.calculate_totals(); + } + + qty() { + this.calculate_totals(); + } + + rate() { + this.calculate_totals(); + } + + company() { + var me = this; + + if (me.frm.doc.company) { + frappe.call({ + method: "frappe.client.get_value", + args: { + doctype: "Company", + filters: {"name": me.frm.doc.company}, + fieldname: "cost_center" + }, + callback: function (r) { + if (r.message) { + $.each(me.frm.doc.service_items || [], function (i, d) { + frappe.model.set_value(d.doctype, d.name, "cost_center", r.message.cost_center); + }); + } + } + }); + } + + erpnext.accounts.dimensions.update_dimension(me.frm, me.frm.doctype); + } + + serivce_items_add(doc, cdt, cdn) { + erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'service_items'); + } + + get_target_item_details() { + var me = this; + + if (me.frm.doc.target_item_code) { + return me.frm.call({ + method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_target_item_details", + child: me.frm.doc, + args: { + item_code: me.frm.doc.target_item_code, + }, + callback: function (r) { + if (!r.exc) { + me.frm.refresh_fields(); + } + } + }); + } + } + + get_target_asset_details() { + var me = this; + + if (me.frm.doc.target_asset) { + return me.frm.call({ + method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_target_asset_details", + child: me.frm.doc, + args: { + asset: me.frm.doc.target_asset + }, + callback: function (r) { + if (!r.exc) { + me.frm.refresh_fields(); + } + } + }); + } + } + + get_consumed_stock_item_details(row) { + var me = this; + + if (row && row.item_code) { + return me.frm.call({ + method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_consumed_stock_item_details", + child: row, + args: { + args: { + item_code: row.item_code, + warehouse: row.warehouse, + stock_qty: flt(row.stock_qty), + doctype: me.frm.doc.doctype, + name: me.frm.doc.name, + company: me.frm.doc.company, + posting_date: me.frm.doc.posting_date, + posting_time: me.frm.doc.posting_time, + } + }, + callback: function (r) { + if (!r.exc) { + me.calculate_totals(); + } + } + }); + } + } + + get_consumed_asset_details(row) { + var me = this; + + if (row && row.asset) { + return me.frm.call({ + method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_consumed_asset_details", + child: row, + args: { + args: { + asset: row.asset, + doctype: me.frm.doc.doctype, + name: me.frm.doc.name, + company: me.frm.doc.company, + finance_book: me.frm.doc.finance_book, + posting_date: me.frm.doc.posting_date, + posting_time: me.frm.doc.posting_time, + } + }, + callback: function (r) { + if (!r.exc) { + me.calculate_totals(); + } + } + }); + } + } + + get_service_item_details(row) { + var me = this; + + if (row && row.item_code) { + return me.frm.call({ + method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_service_item_details", + child: row, + args: { + args: { + item_code: row.item_code, + qty: flt(row.qty), + expense_account: row.expense_account, + company: me.frm.doc.company, + } + }, + callback: function (r) { + if (!r.exc) { + me.calculate_totals(); + } + } + }); + } + } + + get_warehouse_details(item) { + var me = this; + if(item.item_code && item.warehouse) { + me.frm.call({ + method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_warehouse_details", + child: item, + args: { + args: { + 'item_code': item.item_code, + 'warehouse': cstr(item.warehouse), + 'qty': flt(item.stock_qty), + 'serial_no': item.serial_no, + 'posting_date': me.frm.doc.posting_date, + 'posting_time': me.frm.doc.posting_time, + 'company': me.frm.doc.company, + 'voucher_type': me.frm.doc.doctype, + 'voucher_no': me.frm.doc.name, + 'allow_zero_valuation': 1 + } + }, + callback: function(r) { + if (!r.exc) { + me.calculate_totals(); + } + } + }); + } + } + + get_all_item_warehouse_details() { + var me = this; + me.frm.call({ + method: "set_warehouse_details", + doc: me.frm.doc, + callback: function(r) { + if (!r.exc) { + me.calculate_totals(); + } + } + }); + } + + get_all_asset_values() { + var me = this; + me.frm.call({ + method: "set_asset_values", + doc: me.frm.doc, + callback: function(r) { + if (!r.exc) { + me.calculate_totals(); + } + } + }); + } + + calculate_totals() { + var me = this; + + me.frm.doc.stock_items_total = 0; + me.frm.doc.asset_items_total = 0; + me.frm.doc.service_items_total = 0; + + $.each(me.frm.doc.stock_items || [], function (i, d) { + d.amount = flt(flt(d.stock_qty) * flt(d.valuation_rate), precision('amount', d)); + me.frm.doc.stock_items_total += d.amount; + }); + + $.each(me.frm.doc.asset_items || [], function (i, d) { + d.asset_value = flt(flt(d.asset_value), precision('asset_value', d)); + me.frm.doc.asset_items_total += d.asset_value; + }); + + $.each(me.frm.doc.service_items || [], function (i, d) { + d.amount = flt(flt(d.qty) * flt(d.rate), precision('amount', d)); + me.frm.doc.service_items_total += d.amount; + }); + + me.frm.doc.stock_items_total = flt(me.frm.doc.stock_items_total, precision('stock_items_total')); + me.frm.doc.asset_items_total = flt(me.frm.doc.asset_items_total, precision('asset_items_total')); + me.frm.doc.service_items_total = flt(me.frm.doc.service_items_total, precision('service_items_total')); + + me.frm.doc.total_value = me.frm.doc.stock_items_total + me.frm.doc.asset_items_total + me.frm.doc.service_items_total; + me.frm.doc.total_value = flt(me.frm.doc.total_value, precision('total_value')); + + me.frm.refresh_fields(); + } +}; + +//$.extend(cur_frm.cscript, new erpnext.assets.AssetCapitalization({frm: cur_frm})); +cur_frm.cscript = new erpnext.assets.AssetCapitalization({frm: cur_frm}); diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json new file mode 100644 index 0000000000..b697c206bf --- /dev/null +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json @@ -0,0 +1,340 @@ +{ + "actions": [], + "autoname": "naming_series:", + "creation": "2021-09-04 13:38:04.217187", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "target_item_code", + "target_item_name", + "target_is_fixed_asset", + "target_has_batch_no", + "target_has_serial_no", + "entry_type", + "finance_book", + "naming_series", + "column_break_9", + "company", + "posting_date", + "posting_time", + "set_posting_time", + "amended_from", + "target_item_details_section", + "target_asset", + "target_asset_name", + "target_warehouse", + "target_batch_no", + "target_serial_no", + "column_break_5", + "target_qty", + "target_stock_uom", + "section_break_16", + "stock_items", + "stock_items_total", + "section_break_26", + "asset_items", + "asset_items_total", + "service_expenses_section", + "service_items", + "service_items_total", + "totals_section", + "total_value" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title" + }, + { + "fieldname": "target_item_code", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Target Item Code", + "options": "Item", + "reqd": 1 + }, + { + "depends_on": "eval:doc.target_item_code && doc.target_item_name != doc.target_item_code", + "fetch_from": "target_item_code.item_name", + "fieldname": "target_item_name", + "fieldtype": "Data", + "label": "Target Item Name", + "read_only": 1 + }, + { + "default": "0", + "fetch_from": "target_item_code.is_fixed_asset", + "fieldname": "target_is_fixed_asset", + "fieldtype": "Check", + "hidden": 1, + "label": "Target Is Fixed Asset", + "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:!doc.target_item_code || doc.target_is_fixed_asset", + "fieldname": "target_asset", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Target Asset", + "no_copy": 1, + "options": "Asset" + }, + { + "depends_on": "target_asset", + "fetch_from": "target_asset.asset_name", + "fieldname": "target_asset_name", + "fieldtype": "Data", + "label": "Asset Name", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fetch_from": "asset.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Posting Date", + "no_copy": 1, + "reqd": 1, + "search_index": 1 + }, + { + "default": "Now", + "fieldname": "posting_time", + "fieldtype": "Time", + "label": "Posting Time", + "no_copy": 1, + "reqd": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.docstatus==0", + "fieldname": "set_posting_time", + "fieldtype": "Check", + "label": "Edit Posting Date and Time" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "ACC-ASC-.YYYY.-", + "reqd": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Asset Capitalization", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_16", + "fieldtype": "Section Break", + "label": "Consumed Stock Items" + }, + { + "fieldname": "stock_items", + "fieldtype": "Table", + "label": "Stock Items", + "options": "Asset Capitalization Stock Item" + }, + { + "fieldname": "target_item_details_section", + "fieldtype": "Section Break", + "label": "Target Item Details" + }, + { + "depends_on": "eval:!doc.target_is_fixed_asset", + "fieldname": "target_warehouse", + "fieldtype": "Link", + "label": "Target Warehouse", + "options": "Warehouse" + }, + { + "depends_on": "target_has_batch_no", + "fieldname": "target_batch_no", + "fieldtype": "Link", + "label": "Target Batch No", + "options": "Batch" + }, + { + "default": "1", + "fieldname": "target_qty", + "fieldtype": "Float", + "label": "Target Qty", + "read_only_depends_on": "target_is_fixed_asset" + }, + { + "fetch_from": "target_item_code.stock_uom", + "fieldname": "target_stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM", + "read_only": 1 + }, + { + "default": "0", + "fetch_from": "target_item_code.has_batch_no", + "fieldname": "target_has_batch_no", + "fieldtype": "Check", + "hidden": 1, + "label": "Target Has Batch No", + "read_only": 1 + }, + { + "default": "0", + "fetch_from": "target_item_code.has_serial_no", + "fieldname": "target_has_serial_no", + "fieldtype": "Check", + "hidden": 1, + "label": "Target Has Serial No", + "read_only": 1 + }, + { + "depends_on": "target_has_serial_no", + "fieldname": "target_serial_no", + "fieldtype": "Small Text", + "label": "Target Serial No" + }, + { + "fieldname": "section_break_26", + "fieldtype": "Section Break", + "label": "Consumed Asset Items" + }, + { + "fieldname": "asset_items", + "fieldtype": "Table", + "label": "Assets", + "options": "Asset Capitalization Asset Item" + }, + { + "fieldname": "entry_type", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Entry Type", + "options": "\nCapitalization\nDecapitalization", + "read_only": 1 + }, + { + "fieldname": "stock_items_total", + "fieldtype": "Currency", + "label": "Consumed Stock Total Value", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "asset_items_total", + "fieldtype": "Currency", + "label": "Consumed Asset Total Value", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "finance_book", + "fieldtype": "Link", + "label": "Finance Book", + "options": "Finance Book" + }, + { + "fieldname": "service_expenses_section", + "fieldtype": "Section Break", + "label": "Service Expenses" + }, + { + "fieldname": "service_items", + "fieldtype": "Table", + "label": "Services", + "options": "Asset Capitalization Service Item" + }, + { + "fieldname": "service_items_total", + "fieldtype": "Currency", + "label": "Service Expense Total Amount", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "totals_section", + "fieldtype": "Section Break", + "label": "Totals" + }, + { + "fieldname": "total_value", + "fieldtype": "Currency", + "label": "Total Value", + "options": "Company:company:default_currency", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2021-09-06 17:18:31.881006", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Capitalization", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "title", + "track_changes": 1, + "track_seen": 1 +} \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py new file mode 100644 index 0000000000..586710a635 --- /dev/null +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -0,0 +1,402 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from erpnext.controllers.accounts_controller import AccountsController +from frappe.utils import cint, flt +from erpnext.stock.get_item_details import get_item_warehouse, get_default_expense_account, get_default_cost_center +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.setup.doctype.brand.brand import get_brand_defaults +from erpnext.stock.utils import get_incoming_rate +from erpnext.stock.stock_ledger import get_previous_sle +from erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment import get_current_asset_value +from six import string_types +import json + +force_fields = ['target_item_name', 'target_asset_name', 'item_name', 'asset_name', + 'target_is_fixed_asset', 'target_has_serial_no', 'target_has_batch_no', + 'target_stock_uom', 'stock_uom'] + + +class AssetCapitalization(AccountsController): + def validate(self): + self.validate_posting_time() + self.set_missing_values(for_validate=True) + self.set_entry_type() + self.validate_target_item() + self.validate_target_asset() + self.validate_consumed_stock_item() + self.validate_consumed_asset_item() + self.validate_service_item() + self.set_warehouse_details() + self.set_asset_values() + self.calculate_totals() + self.set_title() + + def set_entry_type(self): + self.entry_type = "Capitalization" if self.target_is_fixed_asset else "Decapitalization" + + def set_title(self): + self.title = self.target_asset_name or self.target_item_name or self.target_item_code + + def set_missing_values(self, for_validate=False): + target_item_details = get_target_item_details(self.target_item_code) + for k, v in target_item_details.items(): + if self.meta.has_field(k) and (not self.get(k) or k in force_fields): + self.set(k, v) + + # Remove asset if item not a fixed asset + if not self.target_is_fixed_asset: + self.target_asset = None + + target_asset_details = get_target_asset_details(self.target_asset) + for k, v in target_asset_details.items(): + if self.meta.has_field(k) and (not self.get(k) or k in force_fields): + self.set(k, v) + + for d in self.stock_items: + args = self.as_dict() + args.update(d.as_dict()) + args.doctype = self.doctype + args.name = self.name + consumed_stock_item_details = get_consumed_stock_item_details(args, get_valuation_rate=False) + for k, v in consumed_stock_item_details.items(): + if d.meta.has_field(k) and (not d.get(k) or k in force_fields): + d.set(k, v) + + for d in self.asset_items: + args = self.as_dict() + args.update(d.as_dict()) + args.doctype = self.doctype + args.name = self.name + consumed_asset_details = get_consumed_asset_details(args, get_asset_value=False) + for k, v in consumed_asset_details.items(): + if d.meta.has_field(k) and (not d.get(k) or k in force_fields): + d.set(k, v) + + for d in self.service_items: + args = self.as_dict() + args.update(d.as_dict()) + args.doctype = self.doctype + args.name = self.name + service_item_details = get_service_item_details(args) + for k, v in service_item_details.items(): + if d.meta.has_field(k) and (not d.get(k) or k in force_fields): + d.set(k, v) + + def validate_target_item(self): + target_item = frappe.get_cached_doc("Item", self.target_item_code) + + if not target_item.is_fixed_asset and not target_item.is_stock_item: + frappe.throw(_("Target Item {0} is neither a Fixed Asset nor a Stock Item") + .format(target_item.name)) + + if target_item.is_fixed_asset: + self.target_qty = 1 + + if not target_item.is_stock_item: + self.target_warehouse = None + if not target_item.is_fixed_asset: + self.target_asset = None + if not target_item.has_batch_no: + self.target_batch_no = None + if not target_item.has_serial_no: + self.target_serial_no = "" + + self.validate_item(target_item) + + def validate_target_asset(self): + if self.target_is_fixed_asset and not self.target_asset: + frappe.throw(_("Target Asset is mandatory for Capitalization")) + + if self.target_asset: + target_asset = self.get_asset_for_validation(self.target_asset) + + if target_asset.item_code != self.target_item_code: + frappe.throw(_("Asset {0} does not belong to Item {1}").format(self.target_asset, self.target_item_code)) + + self.validate_asset(target_asset) + + def validate_consumed_stock_item(self): + for d in self.stock_items: + if d.item_code: + item = frappe.get_cached_doc("Item", d.item_code) + + if not item.is_stock_item: + frappe.throw(_("Row #{0}: Item {1} is not a stock item").format(d.idx, d.item_code)) + + if flt(d.stock_qty) <= 0: + frappe.throw(_("Row #{0}: Qty must be a positive number").format(d.idx)) + + self.validate_item(item) + + def validate_consumed_asset_item(self): + for d in self.asset_items: + if d.asset: + if d.asset == self.target_asset: + frappe.throw(_("Row #{0}: Consumed Asset {1} cannot be the same as the Target Asset") + .format(d.idx, d.asset)) + + asset = self.get_asset_for_validation(d.asset) + self.validate_asset(asset) + + def validate_service_item(self): + for d in self.service_items: + if d.item_code: + item = frappe.get_cached_doc("Item", d.item_code) + + if item.is_stock_item or item.is_fixed_asset: + frappe.throw(_("Row #{0}: Item {1} is not a service item").format(d.idx, d.item_code)) + + if flt(d.qty) <= 0: + frappe.throw(_("Row #{0}: Qty must be a positive number").format(d.idx)) + + if flt(d.amount) <= 0: + frappe.throw(_("Row #{0}: Amount must be a positive number").format(d.idx)) + + self.validate_item(item) + + if not d.cost_center: + d.cost_center = frappe.get_cached_value("Company", self.company, "cost_center") + + def validate_item(self, item): + from erpnext.stock.doctype.item.item import validate_end_of_life + validate_end_of_life(item.name, item.end_of_life, item.disabled) + + def get_asset_for_validation(self, asset): + return frappe.db.get_value("Asset", asset, ["name", "item_code", "company", "status", "docstatus"], as_dict=1) + + def validate_asset(self, asset): + if asset.status in ("Draft", "Scrapped", "Sold"): + frappe.throw(_("Asset {0} is {1}").format(asset.name, asset.status)) + + if asset.docstatus == 0: + frappe.throw(_("Asset {0} is Draft").format(asset.name)) + if asset.docstatus == 2: + frappe.throw(_("Asset {0} is cancelled").format(asset.name)) + + if asset.company != self.company: + frappe.throw(_("Asset {0} does not belong to company {1}").format(self.target_asset, self.company)) + + @frappe.whitelist() + def set_warehouse_details(self): + for d in self.stock_items: + if d.item_code and d.warehouse: + args = self.get_args_for_incoming_rate(d) + warehouse_details = get_warehouse_details(args) + d.update(warehouse_details) + + @frappe.whitelist() + def set_asset_values(self): + for d in self.asset_items: + if d.asset: + d.asset_value = flt(get_current_asset_value(d.asset, self.finance_book)) + + def get_args_for_incoming_rate(self, item): + return frappe._dict({ + "item_code": item.item_code, + "warehouse": item.warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "qty": -1 * flt(item.stock_qty), + "serial_no": item.serial_no, + "batch_no": item.batch_no, + "voucher_type": self.doctype, + "voucher_no": self.name, + "company": self.company, + "allow_zero_valuation": cint(item.get('allow_zero_valuation_rate')), + }) + + def calculate_totals(self): + self.stock_items_total = 0 + self.asset_items_total = 0 + self.service_items_total = 0 + + for d in self.stock_items: + d.amount = flt(flt(d.stock_qty) * flt(d.valuation_rate), d.precision('amount')) + self.stock_items_total += d.amount + + for d in self.asset_items: + d.asset_value = flt(flt(d.asset_value), d.precision('asset_value')) + self.asset_items_total += d.asset_value + + for d in self.service_items: + d.amount = flt(flt(d.qty) * flt(d.rate), d.precision('amount')) + self.service_items_total += d.amount + + self.stock_items_total = flt(self.stock_items_total, self.precision('stock_items_total')) + self.asset_items_total = flt(self.asset_items_total, self.precision('asset_items_total')) + self.service_items_total = flt(self.service_items_total, self.precision('service_items_total')) + + self.total_value = self.stock_items_total + self.asset_items_total + self.service_items_total + self.total_value = flt(self.total_value, self.precision('total_value')) + + +@frappe.whitelist() +def get_target_item_details(item_code=None): + out = frappe._dict() + + # Get Item Details + item = frappe._dict() + if item_code: + item = frappe.get_cached_doc("Item", item_code) + + # Set Item Details + out.target_item_name = item.item_name + out.target_stock_uom = item.stock_uom + out.target_is_fixed_asset = cint(item.is_fixed_asset) + out.target_has_batch_no = cint(item.has_batch_no) + out.target_has_serial_no = cint(item.has_serial_no) + + if out.target_is_fixed_asset: + out.target_qty = 1 + out.target_warehouse = None + else: + out.target_asset = None + + if not out.target_has_batch_no: + out.target_batch_no = None + if not out.target_has_serial_no: + out.target_serial_no = "" + + # Set Entry Type + if not item_code: + out.entry_type = "" + elif out.target_is_fixed_asset: + out.entry_type = "Capitalization" + else: + out.entry_type = "Decapitalization" + + return out + + +@frappe.whitelist() +def get_target_asset_details(asset=None): + out = frappe._dict() + + # Get Asset Details + asset_details = frappe._dict() + if asset: + asset_details = frappe.db.get_value("Asset", asset, ['asset_name', 'item_code'], as_dict=1) + if not asset_details: + frappe.throw(_("Asset {0} does not exist").format(asset)) + + # Re-set item code from Asset + out.target_item_code = asset_details.item_code + + # Set Asset Details + out.asset_name = asset_details.asset_name + + return out + + +@frappe.whitelist() +def get_consumed_stock_item_details(args, get_valuation_rate=True): + if isinstance(args, string_types): + args = json.loads(args) + + args = frappe._dict(args) + out = frappe._dict() + + item = frappe._dict() + if args.item_code: + item = frappe.get_cached_doc("Item", args.item_code) + + out.item_name = item.item_name + out.batch_no = None + out.serial_no = "" + + out.stock_qty = flt(args.stock_qty) or 1 + out.stock_uom = item.stock_uom + + out.warehouse = get_item_warehouse(item, args, overwrite_warehouse=True) if item else None + + if get_valuation_rate: + if args.item_code and out.warehouse: + incoming_rate_args = frappe._dict({ + 'item_code': args.item_code, + 'warehouse': out.warehouse, + 'posting_date': args.posting_date, + 'posting_time': args.posting_time, + 'qty': -1 * flt(out.stock_qty), + "voucher_type": args.doctype, + "voucher_no": args.name, + "company": args.company, + }) + out.update(get_warehouse_details(incoming_rate_args)) + else: + out.valuation_rate = 0 + out.actual_qty = 0 + + return out + + +@frappe.whitelist() +def get_warehouse_details(args): + if isinstance(args, string_types): + args = json.loads(args) + + args = frappe._dict(args) + + out = {} + if args.warehouse and args.item_code: + out = { + "actual_qty": get_previous_sle(args).get("qty_after_transaction") or 0, + "valuation_rate": get_incoming_rate(args, raise_error_if_no_rate=False) + } + return out + + +@frappe.whitelist() +def get_consumed_asset_details(args, get_asset_value=True): + if isinstance(args, string_types): + args = json.loads(args) + + args = frappe._dict(args) + out = frappe._dict() + + asset_details = frappe._dict() + if args.asset: + asset_details = frappe.db.get_value("Asset", args.asset, ['asset_name', 'item_code', 'item_name'], as_dict=1) + if not asset_details: + frappe.throw(_("Asset {0} does not exist").format(args.asset)) + + out.item_code = asset_details.item_code + out.asset_name = asset_details.asset_name + out.item_name = asset_details.item_name + + if get_asset_value: + if args.asset: + out.asset_value = flt(get_current_asset_value(args.asset, finance_book=args.finance_book)) + else: + out.asset_value = 0 + + return out + + +@frappe.whitelist() +def get_service_item_details(args): + if isinstance(args, string_types): + args = json.loads(args) + + args = frappe._dict(args) + out = frappe._dict() + + item = frappe._dict() + if args.item_code: + item = frappe.get_cached_doc("Item", args.item_code) + + out.item_name = item.item_name + out.qty = flt(args.qty) or 1 + out.uom = item.purchase_uom or item.stock_uom + + item_defaults = get_item_defaults(item.name, args.company) + item_group_defaults = get_item_group_defaults(item.name, args.company) + brand_defaults = get_brand_defaults(item.name, args.company) + + out.expense_account = get_default_expense_account(args, item_defaults, item_group_defaults, brand_defaults) + out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults) + + return out diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py new file mode 100644 index 0000000000..d8e22c5101 --- /dev/null +++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + +class TestAssetCapitalization(unittest.TestCase): + pass diff --git a/erpnext/assets/doctype/asset_capitalization_asset_item/__init__.py b/erpnext/assets/doctype/asset_capitalization_asset_item/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json new file mode 100644 index 0000000000..a0040338c0 --- /dev/null +++ b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json @@ -0,0 +1,85 @@ +{ + "actions": [], + "creation": "2021-09-05 15:52:10.124538", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset", + "asset_name", + "column_break_3", + "item_code", + "item_name", + "section_break_6", + "asset_value", + "column_break_9" + ], + "fields": [ + { + "fieldname": "asset", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Asset", + "options": "Asset", + "reqd": 1 + }, + { + "fetch_from": "asset.asset_name", + "fieldname": "asset_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Asset Name", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fetch_from": "asset.item_code", + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name", + "read_only": 1 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "label": "Value" + }, + { + "default": "0", + "fieldname": "asset_value", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Asset Value", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-09-06 13:46:04.892863", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Capitalization Asset Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py new file mode 100644 index 0000000000..8817317e70 --- /dev/null +++ b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class AssetCapitalizationAssetItem(Document): + pass diff --git a/erpnext/assets/doctype/asset_capitalization_service_item/__init__.py b/erpnext/assets/doctype/asset_capitalization_service_item/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json new file mode 100644 index 0000000000..2d3584dce4 --- /dev/null +++ b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json @@ -0,0 +1,129 @@ +{ + "actions": [], + "creation": "2021-09-06 13:32:08.642060", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "item_name", + "column_break_3", + "expense_account", + "section_break_6", + "qty", + "uom", + "column_break_9", + "rate", + "amount", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break", + "project" + ], + "fields": [ + { + "bold": 1, + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item" + }, + { + "fetch_from": "item_code.item_name", + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "expense_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Expense Account", + "options": "Account", + "reqd": 1 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "label": "Qty and Rate" + }, + { + "columns": 1, + "default": "1", + "fieldname": "qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Qty", + "non_negative": 1 + }, + { + "columns": 1, + "fetch_from": "stock_item_code.stock_uom", + "fieldname": "uom", + "fieldtype": "Link", + "in_list_view": 1, + "label": "UOM", + "options": "UOM" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fieldname": "rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Rate", + "options": "Company:company:default_currency" + }, + { + "default": "0", + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-09-06 14:06:34.768152", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Capitalization Service Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py new file mode 100644 index 0000000000..fa158295ae --- /dev/null +++ b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class AssetCapitalizationServiceItem(Document): + pass diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/__init__.py b/erpnext/assets/doctype/asset_capitalization_stock_item/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json new file mode 100644 index 0000000000..19c455894a --- /dev/null +++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json @@ -0,0 +1,137 @@ +{ + "actions": [], + "creation": "2021-09-05 15:23:23.492310", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "item_name", + "column_break_3", + "warehouse", + "section_break_6", + "stock_qty", + "stock_uom", + "actual_qty", + "column_break_9", + "valuation_rate", + "amount", + "batch_and_serial_no_section", + "batch_no", + "column_break_13", + "serial_no" + ], + "fields": [ + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Warehouse", + "options": "Warehouse", + "reqd": 1 + }, + { + "fieldname": "batch_no", + "fieldtype": "Link", + "label": "Batch No", + "options": "Batch" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "label": "Qty and Rate" + }, + { + "columns": 1, + "fieldname": "stock_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Qty", + "non_negative": 1 + }, + { + "columns": 1, + "fetch_from": "stock_item_code.stock_uom", + "fieldname": "stock_uom", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Stock UOM", + "options": "UOM", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fieldname": "valuation_rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Valuation Rate", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "batch_and_serial_no_section", + "fieldtype": "Section Break", + "label": "Batch and Serial No" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "fieldname": "serial_no", + "fieldtype": "Small Text", + "label": "Serial No" + }, + { + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item", + "reqd": 1 + }, + { + "fetch_from": "item_code.item_name", + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name", + "read_only": 1 + }, + { + "fieldname": "actual_qty", + "fieldtype": "Float", + "label": "Actual Qty in Warehouse", + "no_copy": 1, + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-09-06 13:46:13.579140", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Capitalization Stock Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py new file mode 100644 index 0000000000..4449538d8e --- /dev/null +++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class AssetCapitalizationStockItem(Document): + pass diff --git a/erpnext/assets/workspace/assets/assets.json b/erpnext/assets/workspace/assets/assets.json index dfbf1a378e..cf437a8375 100644 --- a/erpnext/assets/workspace/assets/assets.json +++ b/erpnext/assets/workspace/assets/assets.json @@ -137,6 +137,17 @@ "onboard": 0, "type": "Link" }, + { + "dependencies": "Asset", + "hidden": 0, + "is_query_report": 0, + "label": "Asset Capitalization", + "link_count": 0, + "link_to": "Asset Capitalization", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, { "hidden": 0, "is_query_report": 0, @@ -179,7 +190,7 @@ "type": "Link" } ], - "modified": "2021-08-05 12:15:54.839452", + "modified": "2021-09-06 16:59:02.668813", "modified_by": "Administrator", "module": "Assets", "name": "Assets", From 702b5c32c1e4c1e30bf9e84ac738df30a3b4a435 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Wed, 8 Sep 2021 16:36:07 +0500 Subject: [PATCH 0002/1047] feat(Asset Capitalization): Accounting Fields --- .../asset_capitalization.js | 43 ++++++++++------ .../asset_capitalization.json | 43 +++++++++++++++- .../asset_capitalization.py | 50 +++++++++++++++++-- .../asset_capitalization_asset_item.json | 31 +++++++++++- .../asset_capitalization_service_item.json | 11 +--- .../asset_capitalization_stock_item.json | 23 ++++++++- 6 files changed, 166 insertions(+), 35 deletions(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js index 9276d00c05..b42634a509 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -148,6 +148,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s this.calculate_totals(); } + target_qty() { + this.calculate_totals(); + } + rate() { this.calculate_totals(); } @@ -156,26 +160,29 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s var me = this; if (me.frm.doc.company) { - frappe.call({ - method: "frappe.client.get_value", - args: { - doctype: "Company", - filters: {"name": me.frm.doc.company}, - fieldname: "cost_center" - }, - callback: function (r) { - if (r.message) { - $.each(me.frm.doc.service_items || [], function (i, d) { - frappe.model.set_value(d.doctype, d.name, "cost_center", r.message.cost_center); - }); - } - } + frappe.model.set_value(me.frm.doc.doctype, me.frm.doc.name, "cost_center", null); + $.each(me.frm.doc.stock_items || [], function (i, d) { + frappe.model.set_value(d.doctype, d.name, "cost_center", null); + }); + $.each(me.frm.doc.asset_items || [], function (i, d) { + frappe.model.set_value(d.doctype, d.name, "cost_center", null); + }); + $.each(me.frm.doc.service_items || [], function (i, d) { + frappe.model.set_value(d.doctype, d.name, "cost_center", null); }); } erpnext.accounts.dimensions.update_dimension(me.frm, me.frm.doctype); } + stock_items_add(doc, cdt, cdn) { + erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'stock_items'); + } + + asset_items_add(doc, cdt, cdn) { + erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'asset_items'); + } + serivce_items_add(doc, cdt, cdn) { erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'service_items'); } @@ -189,6 +196,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s child: me.frm.doc, args: { item_code: me.frm.doc.target_item_code, + company: me.frm.doc.company, }, callback: function (r) { if (!r.exc) { @@ -207,7 +215,8 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_target_asset_details", child: me.frm.doc, args: { - asset: me.frm.doc.target_asset + asset: me.frm.doc.target_asset, + company: me.frm.doc.company, }, callback: function (r) { if (!r.exc) { @@ -381,6 +390,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s me.frm.doc.total_value = me.frm.doc.stock_items_total + me.frm.doc.asset_items_total + me.frm.doc.service_items_total; me.frm.doc.total_value = flt(me.frm.doc.total_value, precision('total_value')); + me.frm.doc.target_qty = flt(me.frm.doc.target_qty, precision('target_qty')); + me.frm.doc.target_incoming_rate = me.frm.doc.target_qty ? me.frm.doc.total_value / flt(me.frm.doc.target_qty) + : me.frm.doc.total_value; + me.frm.refresh_fields(); } }; diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json index b697c206bf..0582b1ebc1 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json @@ -41,7 +41,13 @@ "service_items", "service_items_total", "totals_section", - "total_value" + "total_value", + "column_break_36", + "target_incoming_rate", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break", + "target_fixed_asset_account" ], "fields": [ { @@ -289,12 +295,45 @@ "label": "Total Value", "options": "Company:company:default_currency", "read_only": 1 + }, + { + "fieldname": "column_break_36", + "fieldtype": "Column Break" + }, + { + "fieldname": "target_incoming_rate", + "fieldtype": "Currency", + "label": "Target Incoming Rate", + "options": "Company:company:default_currency" + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "target_fixed_asset_account", + "fieldtype": "Link", + "label": "Target Fixed Asset Account", + "options": "Account", + "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-09-06 17:18:31.881006", + "modified": "2021-09-08 15:58:40.417579", "modified_by": "Administrator", "module": "Assets", "name": "Asset Capitalization", diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 586710a635..64f13887c2 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -11,13 +11,14 @@ from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults from erpnext.setup.doctype.brand.brand import get_brand_defaults from erpnext.stock.utils import get_incoming_rate from erpnext.stock.stock_ledger import get_previous_sle +from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment import get_current_asset_value from six import string_types import json force_fields = ['target_item_name', 'target_asset_name', 'item_name', 'asset_name', 'target_is_fixed_asset', 'target_has_serial_no', 'target_has_batch_no', - 'target_stock_uom', 'stock_uom'] + 'target_stock_uom', 'stock_uom', 'target_fixed_asset_account', 'fixed_asset_account'] class AssetCapitalization(AccountsController): @@ -42,7 +43,7 @@ class AssetCapitalization(AccountsController): self.title = self.target_asset_name or self.target_item_name or self.target_item_code def set_missing_values(self, for_validate=False): - target_item_details = get_target_item_details(self.target_item_code) + target_item_details = get_target_item_details(self.target_item_code, self.company) for k, v in target_item_details.items(): if self.meta.has_field(k) and (not self.get(k) or k in force_fields): self.set(k, v) @@ -51,7 +52,7 @@ class AssetCapitalization(AccountsController): if not self.target_is_fixed_asset: self.target_asset = None - target_asset_details = get_target_asset_details(self.target_asset) + target_asset_details = get_target_asset_details(self.target_asset, self.company) for k, v in target_asset_details.items(): if self.meta.has_field(k) and (not self.get(k) or k in force_fields): self.set(k, v) @@ -95,6 +96,8 @@ class AssetCapitalization(AccountsController): if target_item.is_fixed_asset: self.target_qty = 1 + if flt(self.target_qty) <= 0: + frappe.throw(_("Target Qty must be a positive number")) if not target_item.is_stock_item: self.target_warehouse = None @@ -233,9 +236,12 @@ class AssetCapitalization(AccountsController): self.total_value = self.stock_items_total + self.asset_items_total + self.service_items_total self.total_value = flt(self.total_value, self.precision('total_value')) + self.target_qty = flt(self.target_qty, self.precision('target_qty')) + self.target_incoming_rate = self.total_value / self.target_qty + @frappe.whitelist() -def get_target_item_details(item_code=None): +def get_target_item_details(item_code=None, company=None): out = frappe._dict() # Get Item Details @@ -261,6 +267,13 @@ def get_target_item_details(item_code=None): if not out.target_has_serial_no: out.target_serial_no = "" + # Cost Center + item_defaults = get_item_defaults(item.name, company) + item_group_defaults = get_item_group_defaults(item.name, company) + brand_defaults = get_brand_defaults(item.name, company) + out.cost_center = get_default_cost_center(frappe._dict({'item_code': item.name, 'company': company}), + item_defaults, item_group_defaults, brand_defaults) + # Set Entry Type if not item_code: out.entry_type = "" @@ -273,7 +286,7 @@ def get_target_item_details(item_code=None): @frappe.whitelist() -def get_target_asset_details(asset=None): +def get_target_asset_details(asset=None, company=None): out = frappe._dict() # Get Asset Details @@ -289,6 +302,12 @@ def get_target_asset_details(asset=None): # Set Asset Details out.asset_name = asset_details.asset_name + if asset_details.item_code: + out.target_fixed_asset_account = get_asset_category_account('fixed_asset_account', item=asset_details.item_code, + company=company) + else: + out.target_fixed_asset_account = None + return out @@ -313,6 +332,12 @@ def get_consumed_stock_item_details(args, get_valuation_rate=True): out.warehouse = get_item_warehouse(item, args, overwrite_warehouse=True) if item else None + # Cost Center + item_defaults = get_item_defaults(item.name, args.company) + item_group_defaults = get_item_group_defaults(item.name, args.company) + brand_defaults = get_brand_defaults(item.name, args.company) + out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults) + if get_valuation_rate: if args.item_code and out.warehouse: incoming_rate_args = frappe._dict({ @@ -373,6 +398,21 @@ def get_consumed_asset_details(args, get_asset_value=True): else: out.asset_value = 0 + # Account + if asset_details.item_code: + out.fixed_asset_account = get_asset_category_account('fixed_asset_account', item=asset_details.item_code, + company=args.company) + else: + out.fixed_asset_account = None + + # Cost Center + if asset_details.item_code: + item = frappe.get_cached_doc("Item", asset_details.item_code) + item_defaults = get_item_defaults(item.name, args.company) + item_group_defaults = get_item_group_defaults(item.name, args.company) + brand_defaults = get_brand_defaults(item.name, args.company) + out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults) + return out diff --git a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json index a0040338c0..93ec336b15 100644 --- a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json +++ b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json @@ -12,7 +12,11 @@ "item_name", "section_break_6", "asset_value", - "column_break_9" + "column_break_9", + "accounting_dimensions_section", + "fixed_asset_account", + "dimension_col_break", + "cost_center" ], "fields": [ { @@ -68,12 +72,35 @@ { "fieldname": "column_break_9", "fieldtype": "Column Break" + }, + { + "fieldname": "fixed_asset_account", + "fieldtype": "Link", + "label": "Fixed Asset Account", + "options": "Account", + "read_only": 1 + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-06 13:46:04.892863", + "modified": "2021-09-08 15:54:24.885547", "modified_by": "Administrator", "module": "Assets", "name": "Asset Capitalization Asset Item", diff --git a/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json index 2d3584dce4..0ae1c1428e 100644 --- a/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json +++ b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json @@ -17,8 +17,7 @@ "amount", "accounting_dimensions_section", "cost_center", - "dimension_col_break", - "project" + "dimension_col_break" ], "fields": [ { @@ -106,18 +105,12 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" - }, - { - "fieldname": "project", - "fieldtype": "Link", - "label": "Project", - "options": "Project" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-06 14:06:34.768152", + "modified": "2021-09-08 15:52:08.598100", "modified_by": "Administrator", "module": "Assets", "name": "Asset Capitalization Service Item", diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json index 19c455894a..14eb0f6ef2 100644 --- a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json +++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json @@ -19,7 +19,10 @@ "batch_and_serial_no_section", "batch_no", "column_break_13", - "serial_no" + "serial_no", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break" ], "fields": [ { @@ -120,12 +123,28 @@ "label": "Actual Qty in Warehouse", "no_copy": 1, "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-06 13:46:13.579140", + "modified": "2021-09-08 15:56:20.230548", "modified_by": "Administrator", "module": "Assets", "name": "Asset Capitalization Stock Item", From 3b9bc8e4effcd46d53c30169ea24df2ae849ad5c Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Sun, 12 Sep 2021 14:28:14 +0500 Subject: [PATCH 0003/1047] feat(Asset Capitalization): Finance Book field in Asset Row --- .../asset_capitalization/asset_capitalization.js | 11 ++++++++--- .../asset_capitalization/asset_capitalization.py | 3 ++- .../asset_capitalization_asset_item.json | 13 ++++++++++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js index b42634a509..b0f7712d6e 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -136,8 +136,13 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s } } - finance_book() { - this.get_all_asset_values(); + finance_book(doc, cdt, cdn) { + if (cdt === "Asset Capitalization Asset Item") { + var row = frappe.get_doc(cdt, cdn); + this.get_consumed_asset_details(row); + } else { + this.get_all_asset_values(); + } } stock_qty() { @@ -268,7 +273,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s doctype: me.frm.doc.doctype, name: me.frm.doc.name, company: me.frm.doc.company, - finance_book: me.frm.doc.finance_book, + finance_book: row.finance_book || me.frm.doc.finance_book, posting_date: me.frm.doc.posting_date, posting_time: me.frm.doc.posting_time, } diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 64f13887c2..b29decb2d9 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -72,6 +72,7 @@ class AssetCapitalization(AccountsController): args.update(d.as_dict()) args.doctype = self.doctype args.name = self.name + args.finance_book = d.get('finance_book') or self.get('finance_book') consumed_asset_details = get_consumed_asset_details(args, get_asset_value=False) for k, v in consumed_asset_details.items(): if d.meta.has_field(k) and (not d.get(k) or k in force_fields): @@ -195,7 +196,7 @@ class AssetCapitalization(AccountsController): def set_asset_values(self): for d in self.asset_items: if d.asset: - d.asset_value = flt(get_current_asset_value(d.asset, self.finance_book)) + d.asset_value = flt(get_current_asset_value(d.asset, d.get('finance_book') or self.finance_book)) def get_args_for_incoming_rate(self, item): return frappe._dict({ diff --git a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json index 93ec336b15..a5f820299b 100644 --- a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json +++ b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json @@ -7,6 +7,7 @@ "field_order": [ "asset", "asset_name", + "finance_book", "column_break_3", "item_code", "item_name", @@ -14,9 +15,9 @@ "asset_value", "column_break_9", "accounting_dimensions_section", - "fixed_asset_account", + "cost_center", "dimension_col_break", - "cost_center" + "fixed_asset_account" ], "fields": [ { @@ -95,12 +96,18 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "finance_book", + "fieldtype": "Link", + "label": "Finance Book", + "options": "Finance Book" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-08 15:54:24.885547", + "modified": "2021-09-08 23:42:25.143272", "modified_by": "Administrator", "module": "Assets", "name": "Asset Capitalization Asset Item", From 7a5d75b68d278cf100c9b73ab4ae0475b820eb67 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Mon, 13 Sep 2021 23:01:52 +0500 Subject: [PATCH 0004/1047] feat(Asset Capitalization): Submission and Cancellation --- .../doctype/sales_invoice/sales_invoice.py | 74 +----- erpnext/assets/doctype/asset/asset_list.js | 3 + erpnext/assets/doctype/asset/depreciation.py | 29 ++- .../asset_capitalization.js | 9 +- .../asset_capitalization.py | 217 +++++++++++++++++- .../asset_capitalization_asset_item.json | 11 +- erpnext/controllers/accounts_controller.py | 81 +++++++ 7 files changed, 340 insertions(+), 84 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index ec249c2419..3af7b24b07 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -37,7 +37,6 @@ from erpnext.assets.doctype.asset.depreciation import ( get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain, - post_depreciation_entries, ) from erpnext.controllers.selling_controller import SellingController from erpnext.healthcare.utils import manage_invoice_submit_cancel @@ -166,7 +165,7 @@ class SalesInvoice(SellingController): if self.update_stock: frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale")) - elif asset.status in ("Scrapped", "Cancelled") or (asset.status == "Sold" and not self.is_return): + elif asset.status in ("Scrapped", "Cancelled", "Capitalized", "Decapitalized") or (asset.status == "Sold" and not self.is_return): frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}").format(d.idx, d.asset, asset.status)) def validate_item_cost_centers(self): @@ -1007,77 +1006,6 @@ class SalesInvoice(SellingController): self.check_finance_books(item, asset) return asset - def check_finance_books(self, item, asset): - if (len(asset.finance_books) > 1 and not item.finance_book - and asset.finance_books[0].finance_book): - frappe.throw(_("Select finance book for the item {0} at row {1}") - .format(item.item_code, item.idx)) - - def depreciate_asset(self, asset): - asset.flags.ignore_validate_update_after_submit = True - asset.prepare_depreciation_data(self.posting_date) - asset.save() - - post_depreciation_entries(self.posting_date) - - def reset_depreciation_schedule(self, asset): - asset.flags.ignore_validate_update_after_submit = True - - # recreate original depreciation schedule of the asset - asset.prepare_depreciation_data() - - self.modify_depreciation_schedule_for_asset_repairs(asset) - asset.save() - - self.delete_depreciation_entry_made_after_sale(asset) - - def modify_depreciation_schedule_for_asset_repairs(self, asset): - asset_repairs = frappe.get_all( - 'Asset Repair', - filters = {'asset': asset.name}, - fields = ['name', 'increase_in_asset_life'] - ) - - for repair in asset_repairs: - if repair.increase_in_asset_life: - asset_repair = frappe.get_doc('Asset Repair', repair.name) - asset_repair.modify_depreciation_schedule() - asset.prepare_depreciation_data() - - def delete_depreciation_entry_made_after_sale(self, asset): - from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry - - posting_date_of_original_invoice = self.get_posting_date_of_sales_invoice() - - row = -1 - finance_book = asset.get('schedules')[0].get('finance_book') - for schedule in asset.get('schedules'): - if schedule.finance_book != finance_book: - row = 0 - finance_book = schedule.finance_book - else: - row += 1 - - if schedule.schedule_date == posting_date_of_original_invoice: - if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice): - reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry) - reverse_journal_entry.posting_date = nowdate() - reverse_journal_entry.submit() - - def get_posting_date_of_sales_invoice(self): - return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date') - - # if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone - def sale_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_invoice): - for finance_book in asset.get('finance_books'): - if schedule.finance_book == finance_book.finance_book: - orginal_schedule_date = add_months(finance_book.depreciation_start_date, - row * cint(finance_book.frequency_of_depreciation)) - - if orginal_schedule_date == posting_date_of_original_invoice: - return True - return False - @property def enable_discount_accounting(self): if not hasattr(self, "_enable_discount_accounting"): diff --git a/erpnext/assets/doctype/asset/asset_list.js b/erpnext/assets/doctype/asset/asset_list.js index 4302cb2c51..3d00eb74aa 100644 --- a/erpnext/assets/doctype/asset/asset_list.js +++ b/erpnext/assets/doctype/asset/asset_list.js @@ -10,6 +10,9 @@ frappe.listview_settings['Asset'] = { } else if (doc.status === "Sold") { return [__("Sold"), "green", "status,=,Sold"]; + } else if (["Capitalized", "Decapitalized"].includes(doc.status)) { + return [__(doc.status), "grey", "status,=," + doc.status]; + } else if (doc.status === "Scrapped") { return [__("Scrapped"), "grey", "status,=,Scrapped"]; diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 609791012a..58d4bb5ebb 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -13,7 +13,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( ) -def post_depreciation_entries(date=None): +def post_depreciation_entries(date=None, commit=True): # Return if automatic booking of asset depreciation is disabled if not cint(frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")): return @@ -22,7 +22,8 @@ def post_depreciation_entries(date=None): date = today() for asset in get_depreciable_assets(date): make_depreciation_entry(asset, date) - frappe.db.commit() + if commit: + frappe.db.commit() def get_depreciable_assets(date): return frappe.db.sql_list("""select a.name @@ -140,7 +141,7 @@ def scrap_asset(asset_name): if asset.docstatus != 1: frappe.throw(_("Asset {0} must be submitted").format(asset.name)) - elif asset.status in ("Cancelled", "Sold", "Scrapped"): + elif asset.status in ("Cancelled", "Sold", "Scrapped", "Capitalized", "Decapitalized"): frappe.throw(_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status)) depreciation_series = frappe.get_cached_value('Company', asset.company, "series_for_depreciation_entry") @@ -269,3 +270,25 @@ def get_disposal_account_and_cost_center(company): frappe.throw(_("Please set 'Asset Depreciation Cost Center' in Company {0}").format(company)) return disposal_account, depreciation_cost_center + + +@frappe.whitelist() +def get_value_after_depreciation_on_disposal_date(asset, disposal_date, finance_book=None): + asset_doc = frappe.get_doc("Asset", asset) + + if asset_doc.calculate_depreciation: + asset_doc.prepare_depreciation_data(getdate(disposal_date)) + + finance_book_id = 1 + if finance_book: + for fb in asset_doc.finance_books: + if fb.finance_book == finance_book: + finance_book_id = fb.idx + break + + asset_schedules = [sch for sch in asset_doc.schedules if cint(sch.finance_book_id) == finance_book_id] + accumulated_depr_amount = asset_schedules[-1].accumulated_depreciation_amount + + return flt(flt(asset_doc.gross_purchase_amount) - accumulated_depr_amount, asset_doc.precision('gross_purchase_amount')) + else: + return flt(asset_doc.value_after_depreciation) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js index b0f7712d6e..4f8c95e9a0 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -15,6 +15,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s refresh() { erpnext.hide_company(); + this.show_general_ledger(); + if (this.frm.doc.stock_items || !this.frm.doc.target_is_fixed_asset) { + this.show_stock_ledger(); + } } setup_queries() { @@ -33,7 +37,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s filters['item_code'] = me.frm.doc.target_item_code; } - filters['status'] = ["not in", ["Draft", "Scrapped", "Sold"]] + filters['status'] = ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]] filters['docstatus'] = 1; return { @@ -43,7 +47,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s me.frm.set_query("asset", "asset_items", function() { var filters = { - 'status': ["not in", ["Draft", "Scrapped", "Sold"]], + 'status': ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]], 'docstatus': 1 } @@ -127,6 +131,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s posting_date() { if (this.frm.doc.posting_date) { this.get_all_item_warehouse_details(); + this.get_all_asset_values(); } } diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index b29decb2d9..e50ddfaba8 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -2,8 +2,9 @@ # For license information, please see license.txt import frappe +# import erpnext from frappe import _ -from erpnext.controllers.accounts_controller import AccountsController +from erpnext.controllers.stock_controller import StockController from frappe.utils import cint, flt from erpnext.stock.get_item_details import get_item_warehouse, get_default_expense_account, get_default_cost_center from erpnext.stock.doctype.item.item import get_item_defaults @@ -13,6 +14,9 @@ from erpnext.stock.utils import get_incoming_rate from erpnext.stock.stock_ledger import get_previous_sle from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment import get_current_asset_value +from erpnext.stock import get_warehouse_account_map +from erpnext.assets.doctype.asset.depreciation import get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain,\ + get_value_after_depreciation_on_disposal_date from six import string_types import json @@ -21,7 +25,7 @@ force_fields = ['target_item_name', 'target_asset_name', 'item_name', 'asset_nam 'target_stock_uom', 'stock_uom', 'target_fixed_asset_account', 'fixed_asset_account'] -class AssetCapitalization(AccountsController): +class AssetCapitalization(StockController): def validate(self): self.validate_posting_time() self.set_missing_values(for_validate=True) @@ -36,6 +40,18 @@ class AssetCapitalization(AccountsController): self.calculate_totals() self.set_title() + def before_submit(self): + self.validate_source_mandatory() + + def on_submit(self): + self.update_stock_ledger() + self.make_gl_entries() + + def on_cancel(self): + self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation') + self.update_stock_ledger() + self.make_gl_entries() + def set_entry_type(self): self.entry_type = "Capitalization" if self.target_is_fixed_asset else "Decapitalization" @@ -104,11 +120,15 @@ class AssetCapitalization(AccountsController): self.target_warehouse = None if not target_item.is_fixed_asset: self.target_asset = None + self.target_fixed_asset_account = None if not target_item.has_batch_no: self.target_batch_no = None if not target_item.has_serial_no: self.target_serial_no = "" + if target_item.is_stock_item and not self.target_warehouse: + frappe.throw(_("Target Warehouse is mandatory for Decapitalization")) + self.validate_item(target_item) def validate_target_asset(self): @@ -165,6 +185,13 @@ class AssetCapitalization(AccountsController): if not d.cost_center: d.cost_center = frappe.get_cached_value("Company", self.company, "cost_center") + def validate_source_mandatory(self): + if not self.target_is_fixed_asset and not self.get('asset_items'): + frappe.throw(_("Consumed Asset Items is mandatory for Decapitalization")) + + if not self.get('stock_items') and not self.get('asset_items'): + frappe.throw(_("Consumed Stock Items or Consumed Asset Items is mandatory for Capitalization")) + def validate_item(self, item): from erpnext.stock.doctype.item.item import validate_end_of_life validate_end_of_life(item.name, item.end_of_life, item.disabled) @@ -173,7 +200,7 @@ class AssetCapitalization(AccountsController): return frappe.db.get_value("Asset", asset, ["name", "item_code", "company", "status", "docstatus"], as_dict=1) def validate_asset(self, asset): - if asset.status in ("Draft", "Scrapped", "Sold"): + if asset.status in ("Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"): frappe.throw(_("Asset {0} is {1}").format(asset.name, asset.status)) if asset.docstatus == 0: @@ -196,7 +223,10 @@ class AssetCapitalization(AccountsController): def set_asset_values(self): for d in self.asset_items: if d.asset: - d.asset_value = flt(get_current_asset_value(d.asset, d.get('finance_book') or self.finance_book)) + finance_book = d.get('finance_book') or self.get('finance_book') + d.current_asset_value = flt(get_current_asset_value(d.asset, finance_book=finance_book)) + d.asset_value = get_value_after_depreciation_on_disposal_date(d.asset, self.posting_date, + finance_book=finance_book) def get_args_for_incoming_rate(self, item): return frappe._dict({ @@ -240,6 +270,180 @@ class AssetCapitalization(AccountsController): self.target_qty = flt(self.target_qty, self.precision('target_qty')) self.target_incoming_rate = self.total_value / self.target_qty + def update_stock_ledger(self): + sl_entries = [] + + for d in self.stock_items: + sle = self.get_sl_entries(d, { + "actual_qty": -flt(d.stock_qty), + }) + sl_entries.append(sle) + + if not frappe.db.get_value("Item", self.target_item_code, "is_fixed_asset", cache=1): + sle = self.get_sl_entries(self, { + "item_code": self.target_item_code, + "warehouse": self.target_warehouse, + "batch_no": self.target_batch_no, + "serial_no": self.target_serial_no, + "actual_qty": flt(self.target_qty), + "incoming_rate": flt(self.target_incoming_rate) + }) + sl_entries.append(sle) + + # reverse sl entries if cancel + if self.docstatus == 2: + sl_entries.reverse() + + if sl_entries: + self.make_sl_entries(sl_entries) + + def make_gl_entries(self, gl_entries=None, from_repost=False): + from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries + + if not gl_entries: + gl_entries = self.get_gl_entries() + + if self.docstatus == 1: + if gl_entries: + make_gl_entries(gl_entries, from_repost=from_repost) + elif self.docstatus == 2: + make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) + + def get_gl_entries(self, warehouse_account=None, default_expense_account=None, default_cost_center=None): + # Stock GL Entries + gl_entries = [] + + if not warehouse_account: + warehouse_account = get_warehouse_account_map(self.company) + + precision = self.get_debit_field_precision() + sle_map = self.get_stock_ledger_details() + + if self.target_is_fixed_asset: + target_account = self.target_fixed_asset_account + else: + target_account = warehouse_account[self.target_warehouse]["account"] + + target_against = set() + + # Consumed Stock Items + total_consumed_stock_value = 0 + for item_row in self.stock_items: + sle_list = sle_map.get(item_row.name) + if sle_list: + for sle in sle_list: + stock_value_difference = flt(sle.stock_value_difference, precision) + total_consumed_stock_value += -1 * sle.stock_value_difference + + account = warehouse_account[sle.warehouse]["account"] + target_against.add(account) + + gl_entries.append(self.get_gl_dict({ + "account": account, + "against": target_account, + "cost_center": item_row.cost_center, + "project": item_row.get('project') or self.get('project'), + "remarks": self.get("remarks") or "Accounting Entry for Stock", + "credit": -1 * stock_value_difference, + }, warehouse_account[sle.warehouse]["account_currency"], item=item_row)) + + # Consumed Assets + for item in self.asset_items: + asset = self.get_asset(item) + + if self.docstatus == 2: + fixed_asset_gl_entries = get_gl_entries_on_asset_regain(asset, + item.asset_value, item.get('finance_book') or self.get('finance_book')) + asset.db_set("disposal_date", None) + + self.set_consumed_asset_status(asset) + + if asset.calculate_depreciation: + self.reset_depreciation_schedule(asset) + else: + if asset.calculate_depreciation: + self.depreciate_asset(asset) + + asset.reload() + fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset, + item.asset_value, item.get('finance_book') or self.get('finance_book')) + asset.db_set("disposal_date", self.posting_date) + + self.set_consumed_asset_status(asset) + + for gle in fixed_asset_gl_entries: + gle["against"] = target_account + gl_entries.append(self.get_gl_dict(gle, item=item)) + + # Service Expenses + total_service_expenses = 0 + for item_row in self.service_items: + expense_amount = flt(item_row.amount, precision) + total_service_expenses += expense_amount + target_against.add(item_row.expense_account) + + gl_entries.append(self.get_gl_dict({ + "account": item_row.expense_account, + "against": target_account, + "cost_center": item_row.cost_center, + "project": item_row.get('project') or self.get('project'), + "remarks": self.get("remarks") or "Accounting Entry for Stock", + "credit": expense_amount, + }, item=item_row)) + + target_against = ", ".join(target_against) + total_target_stock_value = 0 + total_target_asset_value = 0 + + if self.target_is_fixed_asset: + # Target Asset Item + total_target_asset_value = flt(self.total_value, precision) + gl_entries.append(self.get_gl_dict({ + "account": self.target_fixed_asset_account, + "against": target_against, + "remarks": self.get("remarks") or _("Accounting Entry for Asset"), + "debit": total_target_asset_value, + "cost_center": self.get('cost_center') + }, item=self)) + + if self.docstatus == 1: + asset_doc = frappe.get_doc("Asset", self.target_asset) + asset_doc.purchase_date = self.posting_date + asset_doc.gross_purchase_amount = total_target_asset_value + asset_doc.purchase_receipt_amount = total_target_asset_value + asset_doc.prepare_depreciation_data() + asset_doc.flags.ignore_validate_update_after_submit = True + asset_doc.save() + else: + # Target Stock Item + sle_list = sle_map.get(self.name) + for sle in sle_list: + stock_value_difference = flt(sle.stock_value_difference, precision) + total_target_stock_value += sle.stock_value_difference + account = warehouse_account[sle.warehouse]["account"] + + gl_entries.append(self.get_gl_dict({ + "account": account, + "against": target_against, + "cost_center": self.cost_center, + "project": self.get('project'), + "remarks": self.get("remarks") or "Accounting Entry for Stock", + "debit": stock_value_difference, + }, warehouse_account[sle.warehouse]["account_currency"], item=self)) + + return gl_entries + + def get_asset(self, item): + asset = frappe.get_doc("Asset", item.asset) + self.check_finance_books(item, asset) + return asset + + def set_consumed_asset_status(self, asset): + if self.docstatus == 1: + asset.set_status("Capitalized" if self.target_is_fixed_asset else "Decapitalized") + else: + asset.set_status() + @frappe.whitelist() def get_target_item_details(item_code=None, company=None): @@ -395,8 +599,11 @@ def get_consumed_asset_details(args, get_asset_value=True): if get_asset_value: if args.asset: - out.asset_value = flt(get_current_asset_value(args.asset, finance_book=args.finance_book)) + out.current_asset_value = flt(get_current_asset_value(args.asset, finance_book=args.finance_book)) + out.asset_value = get_value_after_depreciation_on_disposal_date(args.asset, args.posting_date, + finance_book=args.finance_book) else: + out.current_asset_value = 0 out.asset_value = 0 # Account diff --git a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json index a5f820299b..ebaaffbad1 100644 --- a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json +++ b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json @@ -12,6 +12,7 @@ "item_code", "item_name", "section_break_6", + "current_asset_value", "asset_value", "column_break_9", "accounting_dimensions_section", @@ -102,12 +103,20 @@ "fieldtype": "Link", "label": "Finance Book", "options": "Finance Book" + }, + { + "fieldname": "current_asset_value", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Current Asset Value", + "options": "Company:company:default_currency", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-08 23:42:25.143272", + "modified": "2021-09-12 14:30:02.915132", "modified_by": "Administrator", "module": "Assets", "name": "Asset Capitalization Asset Item", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index b90db054b5..930dca8245 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -54,6 +54,7 @@ from erpnext.stock.get_item_details import ( get_item_tax_map, get_item_warehouse, ) +from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries from erpnext.utilities.transaction_base import TransactionBase @@ -1457,6 +1458,86 @@ class AccountsController(TransactionBase): jv.save() jv.submit() + def check_finance_books(self, item, asset): + if (len(asset.finance_books) > 1 and not item.get('finance_book') and not self.get('finance_book') + and asset.finance_books[0].finance_book): + frappe.throw(_("Select finance book for the item {0} at row {1}") + .format(item.item_code, item.idx)) + + def depreciate_asset(self, asset): + asset.flags.ignore_validate_update_after_submit = True + asset.prepare_depreciation_data(self.posting_date) + asset.save() + + post_depreciation_entries(self.posting_date, commit=False) + + def reset_depreciation_schedule(self, asset): + asset.flags.ignore_validate_update_after_submit = True + + # recreate original depreciation schedule of the asset + asset.prepare_depreciation_data() + + self.modify_depreciation_schedule_for_asset_repairs(asset) + asset.save() + + self.delete_depreciation_entry_made_after_disposal(asset) + + def modify_depreciation_schedule_for_asset_repairs(self, asset): + asset_repairs = frappe.get_all( + 'Asset Repair', + filters={'asset': asset.name}, + fields=['name', 'increase_in_asset_life'] + ) + + for repair in asset_repairs: + if repair.increase_in_asset_life: + asset_repair = frappe.get_doc('Asset Repair', repair.name) + asset_repair.modify_depreciation_schedule() + asset.prepare_depreciation_data() + + def delete_depreciation_entry_made_after_disposal(self, asset): + from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry + + posting_date_of_original_invoice = self.get_posting_date_of_disposal_entry() + + row = -1 + finance_book = asset.get('schedules')[0].get('finance_book') + for schedule in asset.get('schedules'): + if schedule.finance_book != finance_book: + row = 0 + finance_book = schedule.finance_book + else: + row += 1 + + if schedule.schedule_date == posting_date_of_original_invoice: + if not self.disposal_was_made_on_original_schedule_date(asset, schedule, row, + posting_date_of_original_invoice): + reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry) + reverse_journal_entry.posting_date = nowdate() + + for d in reverse_journal_entry.accounts: + d.reference_type = "Asset" + d.reference_name = asset.name + + reverse_journal_entry.submit() + + def get_posting_date_of_disposal_entry(self): + if self.doctype == "Sales Invoice" and self.return_against: + return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date') + else: + return self.posting_date + + # if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone + def disposal_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_disposal): + for finance_book in asset.get('finance_books'): + if schedule.finance_book == finance_book.finance_book: + orginal_schedule_date = add_months(finance_book.depreciation_start_date, + row * cint(finance_book.frequency_of_depreciation)) + + if orginal_schedule_date == posting_date_of_original_disposal: + return True + return False + @frappe.whitelist() def get_tax_rate(account_head): return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True) From 8c54be7e999e2c5e7a61ae69a2bae8e624a939b0 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Tue, 14 Sep 2021 12:30:40 +0500 Subject: [PATCH 0005/1047] chore(Asset Capitalization): linting --- .../asset_capitalization.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js index 4f8c95e9a0..892f8c7d4a 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -37,27 +37,27 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s filters['item_code'] = me.frm.doc.target_item_code; } - filters['status'] = ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]] + filters['status'] = ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]]; filters['docstatus'] = 1; return { filters: filters - } + }; }); me.frm.set_query("asset", "asset_items", function() { var filters = { 'status': ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]], 'docstatus': 1 - } + }; if (me.frm.doc.target_asset) { - filters['name'] = ['!=', me.frm.doc.target_asset] + filters['name'] = ['!=', me.frm.doc.target_asset]; } return { filters: filters - } + }; }); me.frm.set_query("item_code", "stock_items", function() { @@ -70,19 +70,19 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s me.frm.set_query('batch_no', 'stock_items', function(doc, cdt, cdn) { var item = locals[cdt][cdn]; - if(!item.item_code) { + if (!item.item_code) { frappe.throw(__("Please enter Item Code to get Batch Number")); } else { var filters = { 'item_code': item.item_code, 'posting_date': me.frm.doc.posting_date || frappe.datetime.nowdate(), 'warehouse': item.warehouse - } + }; return { - query : "erpnext.controllers.queries.get_batch_no", + query: "erpnext.controllers.queries.get_batch_no", filters: filters - } + }; } }); @@ -318,7 +318,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s get_warehouse_details(item) { var me = this; - if(item.item_code && item.warehouse) { + if (item.item_code && item.warehouse) { me.frm.call({ method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_warehouse_details", child: item, From dc24a657fd2c71f03c0f4f6d5a0c1460152725ca Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Tue, 14 Sep 2021 12:40:17 +0500 Subject: [PATCH 0006/1047] chore(Asset Capitalization): linting --- .../doctype/sales_invoice/sales_invoice.py | 1 - .../asset_capitalization/asset_capitalization.py | 16 +++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 3af7b24b07..b2188e1e5e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -10,7 +10,6 @@ from frappe.model.mapper import get_mapped_doc from frappe.model.utils import get_fetch_values from frappe.utils import ( add_days, - add_months, cint, cstr, flt, diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index e50ddfaba8..856ace2ee5 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -5,8 +5,15 @@ import frappe # import erpnext from frappe import _ from erpnext.controllers.stock_controller import StockController -from frappe.utils import cint, flt -from erpnext.stock.get_item_details import get_item_warehouse, get_default_expense_account, get_default_cost_center +from frappe.utils import ( + cint, + flt +) +from erpnext.stock.get_item_details import ( + get_item_warehouse, + get_default_expense_account, + get_default_cost_center +) 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.setup.doctype.brand.brand import get_brand_defaults @@ -15,8 +22,11 @@ from erpnext.stock.stock_ledger import get_previous_sle from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment import get_current_asset_value from erpnext.stock import get_warehouse_account_map -from erpnext.assets.doctype.asset.depreciation import get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain,\ +from erpnext.assets.doctype.asset.depreciation import ( + get_gl_entries_on_asset_disposal, + get_gl_entries_on_asset_regain, get_value_after_depreciation_on_disposal_date +) from six import string_types import json From 8873ef7b675965f920d0534caa981091910d9d4b Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Tue, 14 Sep 2021 15:05:39 +0500 Subject: [PATCH 0007/1047] chore(Asset Capitalization): isort linting --- .../asset_capitalization.py | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 856ace2ee5..129b1aa4e1 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -1,34 +1,36 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +import json + import frappe + # import erpnext from frappe import _ -from erpnext.controllers.stock_controller import StockController -from frappe.utils import ( - cint, - flt -) -from erpnext.stock.get_item_details import ( - get_item_warehouse, - get_default_expense_account, - get_default_cost_center -) -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.setup.doctype.brand.brand import get_brand_defaults -from erpnext.stock.utils import get_incoming_rate -from erpnext.stock.stock_ledger import get_previous_sle -from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account -from erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment import get_current_asset_value -from erpnext.stock import get_warehouse_account_map +from frappe.utils import cint, flt +from six import string_types + from erpnext.assets.doctype.asset.depreciation import ( get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain, - get_value_after_depreciation_on_disposal_date + get_value_after_depreciation_on_disposal_date, ) -from six import string_types -import json +from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account +from erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment import ( + get_current_asset_value, +) +from erpnext.controllers.stock_controller import StockController +from erpnext.setup.doctype.brand.brand import get_brand_defaults +from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults +from erpnext.stock import get_warehouse_account_map +from erpnext.stock.doctype.item.item import get_item_defaults +from erpnext.stock.get_item_details import ( + get_default_cost_center, + get_default_expense_account, + get_item_warehouse, +) +from erpnext.stock.stock_ledger import get_previous_sle +from erpnext.stock.utils import get_incoming_rate force_fields = ['target_item_name', 'target_asset_name', 'item_name', 'asset_name', 'target_is_fixed_asset', 'target_has_serial_no', 'target_has_batch_no', From 9ae0380a96ebe7521e28262c5d9e10914d45de18 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Tue, 14 Sep 2021 15:09:58 +0500 Subject: [PATCH 0008/1047] chore(Asset Capitalization): isort linting --- .../accounts/doctype/sales_invoice/sales_invoice.py | 11 +---------- erpnext/controllers/accounts_controller.py | 2 +- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index b2188e1e5e..14f4787cda 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -8,16 +8,7 @@ from frappe import _, msgprint, throw from frappe.contacts.doctype.address.address import get_address_display from frappe.model.mapper import get_mapped_doc from frappe.model.utils import get_fetch_values -from frappe.utils import ( - add_days, - cint, - cstr, - flt, - formatdate, - get_link_to_form, - getdate, - nowdate, -) +from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate from six import iteritems import erpnext diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 930dca8245..31af7f3744 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -38,6 +38,7 @@ from erpnext.accounts.party import ( validate_party_frozen_disabled, ) from erpnext.accounts.utils import get_account_currency, get_fiscal_years, validate_fiscal_year +from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries from erpnext.buying.utils import update_last_purchase_rate from erpnext.controllers.print_settings import ( set_print_templates_for_item_table, @@ -54,7 +55,6 @@ from erpnext.stock.get_item_details import ( get_item_tax_map, get_item_warehouse, ) -from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries from erpnext.utilities.transaction_base import TransactionBase From d173e06e69feb60f59e7c43b7daa6752a3236db8 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Tue, 14 Sep 2021 15:13:35 +0500 Subject: [PATCH 0009/1047] chore(Asset Capitalization): isort linting --- .../doctype/asset_capitalization/test_asset_capitalization.py | 1 + .../asset_capitalization_asset_item.py | 1 + .../asset_capitalization_service_item.py | 1 + .../asset_capitalization_stock_item.py | 1 + 4 files changed, 4 insertions(+) diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py index d8e22c5101..da128467fe 100644 --- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py @@ -4,5 +4,6 @@ # import frappe import unittest + class TestAssetCapitalization(unittest.TestCase): pass diff --git a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py index 8817317e70..ba356d6b9f 100644 --- a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py +++ b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py @@ -4,5 +4,6 @@ # import frappe from frappe.model.document import Document + class AssetCapitalizationAssetItem(Document): pass diff --git a/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py index fa158295ae..28d018ee39 100644 --- a/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py +++ b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py @@ -4,5 +4,6 @@ # import frappe from frappe.model.document import Document + class AssetCapitalizationServiceItem(Document): pass diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py index 4449538d8e..5d6f98d5cf 100644 --- a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py +++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py @@ -4,5 +4,6 @@ # import frappe from frappe.model.document import Document + class AssetCapitalizationStockItem(Document): pass From 132b517584352a8a6c895fd3256bdc08b03c1df5 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Thu, 16 Sep 2021 23:20:36 +0500 Subject: [PATCH 0010/1047] fix(Asset Captalization): run_serially on posting_date changed --- .../asset_capitalization/asset_capitalization.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js index 892f8c7d4a..d135e60ae7 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -16,7 +16,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s refresh() { erpnext.hide_company(); this.show_general_ledger(); - if (this.frm.doc.stock_items || !this.frm.doc.target_is_fixed_asset) { + if ((this.frm.doc.stock_items && this.frm.doc.stock_items.length) || !this.frm.doc.target_is_fixed_asset) { this.show_stock_ledger(); } } @@ -130,8 +130,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s posting_date() { if (this.frm.doc.posting_date) { - this.get_all_item_warehouse_details(); - this.get_all_asset_values(); + frappe.run_serially([ + () => this.get_all_item_warehouse_details(), + () => this.get_all_asset_values() + ]); } } @@ -347,7 +349,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s get_all_item_warehouse_details() { var me = this; - me.frm.call({ + return me.frm.call({ method: "set_warehouse_details", doc: me.frm.doc, callback: function(r) { @@ -360,7 +362,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s get_all_asset_values() { var me = this; - me.frm.call({ + return me.frm.call({ method: "set_asset_values", doc: me.frm.doc, callback: function(r) { From 003cfe27172f1f1eecf1e663b7c0559c1a009be6 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Thu, 16 Sep 2021 23:21:09 +0500 Subject: [PATCH 0011/1047] fix(Asset Capitalization): Hide source items section if table is empty --- .../asset_capitalization/asset_capitalization.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json index 0582b1ebc1..d7e6b54716 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json @@ -158,6 +158,7 @@ "read_only": 1 }, { + "depends_on": "eval:doc.docstatus == 0 || (doc.stock_items && doc.stock_items.length)", "fieldname": "section_break_16", "fieldtype": "Section Break", "label": "Consumed Stock Items" @@ -227,6 +228,7 @@ "label": "Target Serial No" }, { + "depends_on": "eval:doc.docstatus == 0 || (doc.asset_items && doc.asset_items.length)", "fieldname": "section_break_26", "fieldtype": "Section Break", "label": "Consumed Asset Items" @@ -267,6 +269,7 @@ "options": "Finance Book" }, { + "depends_on": "eval:doc.docstatus == 0 || (doc.service_items && doc.service_items.length)", "fieldname": "service_expenses_section", "fieldtype": "Section Break", "label": "Service Expenses" @@ -304,7 +307,8 @@ "fieldname": "target_incoming_rate", "fieldtype": "Currency", "label": "Target Incoming Rate", - "options": "Company:company:default_currency" + "options": "Company:company:default_currency", + "read_only": 1 }, { "collapsible": 1, @@ -333,7 +337,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-09-08 15:58:40.417579", + "modified": "2021-09-15 15:41:27.917458", "modified_by": "Administrator", "module": "Assets", "name": "Asset Capitalization", From e832944dfede4e979691ec7a4dd22c2fd89de4f7 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Thu, 16 Sep 2021 23:22:31 +0500 Subject: [PATCH 0012/1047] fix(Asset): On Depreciation reversal, remove Journal Entry reference --- erpnext/controllers/accounts_controller.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 31af7f3744..ba0c7a8a8b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1475,13 +1475,12 @@ class AccountsController(TransactionBase): asset.flags.ignore_validate_update_after_submit = True # recreate original depreciation schedule of the asset + self.delete_depreciation_entry_made_after_disposal(asset) asset.prepare_depreciation_data() self.modify_depreciation_schedule_for_asset_repairs(asset) asset.save() - self.delete_depreciation_entry_made_after_disposal(asset) - def modify_depreciation_schedule_for_asset_repairs(self, asset): asset_repairs = frappe.get_all( 'Asset Repair', @@ -1511,7 +1510,7 @@ class AccountsController(TransactionBase): if schedule.schedule_date == posting_date_of_original_invoice: if not self.disposal_was_made_on_original_schedule_date(asset, schedule, row, - posting_date_of_original_invoice): + posting_date_of_original_invoice) or getdate(schedule.schedule_date) > getdate(today()): reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry) reverse_journal_entry.posting_date = nowdate() @@ -1520,6 +1519,8 @@ class AccountsController(TransactionBase): d.reference_name = asset.name reverse_journal_entry.submit() + schedule.db_set('journal_entry', None) + def get_posting_date_of_disposal_entry(self): if self.doctype == "Sales Invoice" and self.return_against: From c311b8ea4f5c51c18462f671d26ede1c18f7f5b6 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Thu, 16 Sep 2021 23:23:22 +0500 Subject: [PATCH 0013/1047] fix(Asset Capitalization): validation edge cases --- .../doctype/asset_capitalization/asset_capitalization.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 129b1aa4e1..5a2398650b 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -189,7 +189,7 @@ class AssetCapitalization(StockController): if flt(d.qty) <= 0: frappe.throw(_("Row #{0}: Qty must be a positive number").format(d.idx)) - if flt(d.amount) <= 0: + if flt(d.rate) <= 0: frappe.throw(_("Row #{0}: Amount must be a positive number").format(d.idx)) self.validate_item(item) @@ -221,11 +221,11 @@ class AssetCapitalization(StockController): frappe.throw(_("Asset {0} is cancelled").format(asset.name)) if asset.company != self.company: - frappe.throw(_("Asset {0} does not belong to company {1}").format(self.target_asset, self.company)) + frappe.throw(_("Asset {0} does not belong to company {1}").format(asset.name, self.company)) @frappe.whitelist() def set_warehouse_details(self): - for d in self.stock_items: + for d in self.get('stock_items'): if d.item_code and d.warehouse: args = self.get_args_for_incoming_rate(d) warehouse_details = get_warehouse_details(args) @@ -233,7 +233,7 @@ class AssetCapitalization(StockController): @frappe.whitelist() def set_asset_values(self): - for d in self.asset_items: + for d in self.get('asset_items'): if d.asset: finance_book = d.get('finance_book') or self.get('finance_book') d.current_asset_value = flt(get_current_asset_value(d.asset, finance_book=finance_book)) From 86a6293e6226fbadbdb28ce681d2b39a77e3acc2 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Thu, 16 Sep 2021 23:24:46 +0500 Subject: [PATCH 0014/1047] test(Asset Capitalization): unit tests --- erpnext/assets/doctype/asset/test_asset.py | 6 +- .../test_asset_capitalization.py | 329 +++++++++++++++++- 2 files changed, 330 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 4cc9be5b05..2bb5a09908 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -702,9 +702,9 @@ def create_asset(**args): "company": args.company or"_Test Company", "purchase_date": "2015-01-01", "calculate_depreciation": args.calculate_depreciation or 0, - "gross_purchase_amount": 100000, - "purchase_receipt_amount": 100000, - "expected_value_after_useful_life": 10000, + "gross_purchase_amount": args.asset_value or 100000, + "purchase_receipt_amount": args.asset_value or 100000, + "expected_value_after_useful_life": args.asset_value or 10000, "warehouse": args.warehouse or "_Test Warehouse - _TC", "available_for_use_date": "2020-06-06", "location": "Test Location", diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py index da128467fe..9bfc88b28c 100644 --- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py @@ -1,9 +1,334 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -# import frappe import unittest +import frappe +from frappe.utils import cint, flt, getdate, now_datetime + +from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries +from erpnext.assets.doctype.asset.test_asset import ( + create_asset, + create_asset_data, + set_depreciation_settings_in_company, +) +from erpnext.stock.doctype.item.test_item import create_item + class TestAssetCapitalization(unittest.TestCase): - pass + def setUp(self): + set_depreciation_settings_in_company() + create_asset_data() + create_asset_capitalization_data() + frappe.db.sql("delete from `tabTax Rule`") + + def test_capitalization(self): + # Variables + consumed_asset_value = 100_000 + + stock_rate = 1000 + stock_qty = 2 + stock_amount = 2000 + + service_rate = 500 + service_qty = 2 + service_amount = 1000 + + total_amount = 103_000 + + # Create assets + target_asset = create_asset(asset_name='Asset Capitalization Target Asset', submit=1) + consumed_asset = create_asset(asset_name='Asset Capitalization Consumable Asset', asset_value=consumed_asset_value, + submit=1) + + # Create and submit Asset Captitalization + asset_capitalization = create_asset_capitalization(target_asset=target_asset.name, + stock_qty=stock_qty, stock_rate=stock_rate, + consumed_asset=consumed_asset.name, + service_qty=service_qty, service_rate=service_rate, + service_expense_account='Expenses Included In Asset Valuation - _TC', + submit=1) + + # Test Asset Capitalization values + self.assertEqual(asset_capitalization.entry_type, 'Capitalization') + self.assertEqual(asset_capitalization.target_qty, 1) + + self.assertEqual(asset_capitalization.stock_items[0].valuation_rate, stock_rate) + self.assertEqual(asset_capitalization.stock_items[0].amount, stock_amount) + self.assertEqual(asset_capitalization.stock_items_total, stock_amount) + + self.assertEqual(asset_capitalization.asset_items[0].asset_value, consumed_asset_value) + self.assertEqual(asset_capitalization.asset_items_total, consumed_asset_value) + + self.assertEqual(asset_capitalization.service_items[0].amount, service_amount) + self.assertEqual(asset_capitalization.service_items_total, service_amount) + + self.assertEqual(asset_capitalization.total_value, total_amount) + self.assertEqual(asset_capitalization.target_incoming_rate, total_amount) + + # Test Target Asset values + target_asset.reload() + self.assertEqual(target_asset.gross_purchase_amount, total_amount) + self.assertEqual(target_asset.purchase_receipt_amount, total_amount) + + # Test Consumed Asset values + self.assertEqual(consumed_asset.db_get('status'), 'Capitalized') + + # Test General Ledger Entries + expected_gle = { + '_Test Fixed Asset - _TC': 3000, + 'Expenses Included In Asset Valuation - _TC': -1000, + 'Stock In Hand - _TC' : -2000 + } + actual_gle = get_actual_gle_dict(asset_capitalization.name) + + self.assertEqual(actual_gle, expected_gle) + + # Test Stock Ledger Entries + expected_sle = { + ('Capitalization Source Stock Item', '_Test Warehouse - _TC'): { + 'actual_qty': -stock_qty, 'stock_value_difference': -stock_amount + } + } + actual_sle = get_actual_sle_dict(asset_capitalization.name) + + self.assertEqual(actual_sle, expected_sle) + + # Cancel Asset Capitalization and make test entries and status are reversed + asset_capitalization.cancel() + self.assertEqual(consumed_asset.db_get('status'), 'Submitted') + self.assertFalse(get_actual_gle_dict(asset_capitalization.name)) + self.assertFalse(get_actual_sle_dict(asset_capitalization.name)) + + def test_decapitalization_with_depreciation(self): + # Variables + purchase_date = '2020-01-01' + depreciation_start_date = '2020-12-31' + capitalization_date = '2021-06-30' + + total_number_of_depreciations = 3 + expected_value_after_useful_life = 10_000 + consumed_asset_purchase_value = 100_000 + consumed_asset_current_value = 70_000 + consumed_asset_value_before_disposal = 55_000 + + target_qty = 10 + target_incoming_rate = 5500 + + depreciation_before_disposal_amount = 15_000 + accumulated_depreciation = 45_000 + + # to accomodate for depreciation on disposal calculation bugs TODO remove this when bug is fixed + consumed_asset_value_before_disposal = 60_082.19 + target_incoming_rate = 6008.219 + depreciation_before_disposal_amount = 9917.81 + accumulated_depreciation = 39_917.81 + + # Create assets + consumed_asset = create_depreciation_asset( + asset_name='Asset Capitalization Consumable Asset', + asset_value=consumed_asset_purchase_value, + purchase_date=purchase_date, + depreciation_start_date=depreciation_start_date, + depreciation_method='Straight Line', + total_number_of_depreciations=total_number_of_depreciations, + frequency_of_depreciation=12, + expected_value_after_useful_life=expected_value_after_useful_life, + submit=1) + + # Create and submit Asset Captitalization + asset_capitalization = create_asset_capitalization( + posting_date=capitalization_date, # half a year + target_item_code="Capitalization Target Stock Item", + target_qty=target_qty, + consumed_asset=consumed_asset.name, + submit=1) + + # Test Asset Capitalization values + self.assertEqual(asset_capitalization.entry_type, 'Decapitalization') + + self.assertEqual(asset_capitalization.asset_items[0].current_asset_value, consumed_asset_current_value) + self.assertEqual(asset_capitalization.asset_items[0].asset_value, consumed_asset_value_before_disposal) + self.assertEqual(asset_capitalization.asset_items_total, consumed_asset_value_before_disposal) + + self.assertEqual(asset_capitalization.total_value, consumed_asset_value_before_disposal) + self.assertEqual(asset_capitalization.target_incoming_rate, target_incoming_rate) + + # Test Consumed Asset values + consumed_asset.reload() + self.assertEqual(consumed_asset.status, 'Decapitalized') + + consumed_depreciation_schedule = [d for d in consumed_asset.schedules + if getdate(d.schedule_date) == getdate(capitalization_date)] + self.assertTrue(consumed_depreciation_schedule and consumed_depreciation_schedule[0].journal_entry) + self.assertEqual(consumed_depreciation_schedule[0].depreciation_amount, depreciation_before_disposal_amount) + + # Test General Ledger Entries + expected_gle = { + 'Stock In Hand - _TC': consumed_asset_value_before_disposal, + '_Test Accumulated Depreciations - _TC': accumulated_depreciation, + '_Test Fixed Asset - _TC': -consumed_asset_purchase_value, + } + actual_gle = get_actual_gle_dict(asset_capitalization.name) + + self.assertEqual(actual_gle, expected_gle) + + # Cancel Asset Capitalization and make test entries and status are reversed + asset_capitalization.cancel() + self.assertEqual(consumed_asset.db_get('status'), 'Partially Depreciated') + self.assertFalse(get_actual_gle_dict(asset_capitalization.name)) + self.assertFalse(get_actual_sle_dict(asset_capitalization.name)) + + +def create_asset_capitalization_data(): + create_item("Capitalization Target Stock Item", + is_stock_item=1, is_fixed_asset=0, is_purchase_item=0) + create_item("Capitalization Source Stock Item", + is_stock_item=1, is_fixed_asset=0, is_purchase_item=0) + create_item("Capitalization Source Service Item", + is_stock_item=0, is_fixed_asset=0, is_purchase_item=0) + + +def create_asset_capitalization(**args): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + args = frappe._dict(args) + + now = now_datetime() + target_asset = frappe.get_doc("Asset", args.target_asset) if args.target_asset else frappe._dict() + target_item_code = target_asset.item_code or args.target_item_code + company = target_asset.company or args.company or "_Test Company" + warehouse = args.warehouse or create_warehouse("_Test Warehouse", company=company) + target_warehouse = args.target_warehouse or warehouse + source_warehouse = args.source_warehouse or warehouse + + asset_capitalization = frappe.new_doc("Asset Capitalization") + asset_capitalization.update({ + "company": company, + "posting_date": args.posting_date or now.strftime('%Y-%m-%d'), + "posting_time": args.posting_time or now.strftime('%H:%M:%S.%f'), + "target_item_code": target_item_code, + "target_asset": target_asset.name, + "target_warehouse": target_warehouse, + "target_qty": flt(args.target_qty) or 1, + "target_batch_no": args.target_batch_no, + "target_serial_no": args.target_serial_no, + "finance_book": args.finance_book + }) + + if args.posting_date or args.posting_time: + asset_capitalization.set_posting_time = 1 + + if flt(args.stock_rate): + asset_capitalization.append("stock_items", { + "item_code": args.stock_item or "Capitalization Source Stock Item", + "warehouse": source_warehouse, + "stock_qty": flt(args.stock_qty) or 1, + "batch_no": args.stock_batch_no, + "serial_no": args.stock_serial_no, + }) + + if args.consumed_asset: + asset_capitalization.append("asset_items", { + "asset": args.consumed_asset, + }) + + if flt(args.service_rate): + asset_capitalization.append("service_items", { + "item_code": args.service_item or "Capitalization Source Service Item", + "expense_account": args.service_expense_account, + "qty": flt(args.service_qty) or 1, + "rate": flt(args.service_rate) + }) + + if args.submit: + create_stock_reconciliation(asset_capitalization, stock_rate=args.stock_rate) + + asset_capitalization.insert() + + if args.submit: + asset_capitalization.submit() + + return asset_capitalization + + +def create_stock_reconciliation(asset_capitalization, stock_rate=0): + from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( + create_stock_reconciliation, + ) + if not asset_capitalization.get('stock_items'): + return + + return create_stock_reconciliation( + item_code=asset_capitalization.stock_items[0].item_code, + warehouse=asset_capitalization.stock_items[0].warehouse, + qty=flt(asset_capitalization.stock_items[0].stock_qty), + rate=flt(stock_rate), + company=asset_capitalization.company) + + +def create_depreciation_asset(**args): + args = frappe._dict(args) + + asset = frappe.new_doc("Asset") + asset.is_existing_asset = 1 + asset.calculate_depreciation = 1 + asset.asset_owner = "Company" + + asset.company = args.company or "_Test Company" + asset.item_code = args.item_code or "Macbook Pro" + asset.asset_name = args.asset_name or asset.item_code + asset.location = args.location or "Test Location" + + asset.purchase_date = args.purchase_date or '2020-01-01' + asset.available_for_use_date = args.available_for_use_date or asset.purchase_date + + asset.gross_purchase_amount = args.asset_value or 100000 + asset.purchase_receipt_amount = asset.gross_purchase_amount + + finance_book = asset.append('finance_books') + finance_book.depreciation_start_date = args.depreciation_start_date or '2020-12-31' + finance_book.depreciation_method = args.depreciation_method or 'Straight Line' + finance_book.total_number_of_depreciations = cint(args.total_number_of_depreciations) or 3 + finance_book.frequency_of_depreciation = cint(args.frequency_of_depreciation) or 12 + finance_book.expected_value_after_useful_life = flt(args.expected_value_after_useful_life) + + if args.submit: + asset.submit() + + frappe.db.set_value("Company", "_Test Company", "series_for_depreciation_entry", "DEPR-") + post_depreciation_entries(date=finance_book.depreciation_start_date) + asset.load_from_db() + + return asset + + +def get_actual_gle_dict(name): + return dict(frappe.db.sql(""" + select account, sum(debit-credit) as diff + from `tabGL Entry` + where voucher_type = 'Asset Capitalization' and voucher_no = %s + group by account + having diff != 0 + """, name)) + + +def get_actual_sle_dict(name): + sles = frappe.db.sql(""" + select + item_code, warehouse, + sum(actual_qty) as actual_qty, + sum(stock_value_difference) as stock_value_difference + from `tabStock Ledger Entry` + where voucher_type = 'Asset Capitalization' and voucher_no = %s + group by item_code, warehouse + having actual_qty != 0 + """, name, as_dict=1) + + sle_dict = {} + for d in sles: + sle_dict[(d.item_code, d.warehouse)] = { + 'actual_qty': d.actual_qty, 'stock_value_difference': d.stock_value_difference + } + + return sle_dict From dc3c27fd1b6382c1f1e7a6cdbe4af3826859b247 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Thu, 4 Nov 2021 13:47:33 +0500 Subject: [PATCH 0015/1047] fix(Asset Capitalization): update code for changes in depreciation logic --- .../doctype/sales_invoice/sales_invoice.py | 86 +------------------ erpnext/assets/doctype/asset/asset.py | 14 +-- .../asset_capitalization.py | 2 +- .../test_asset_capitalization.py | 10 +-- erpnext/controllers/accounts_controller.py | 41 +++++---- 5 files changed, 37 insertions(+), 116 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 2b3850e513..7ed45ce26c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -27,7 +27,6 @@ from erpnext.assets.doctype.asset.depreciation import ( get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain, - make_depreciation_entry, ) from erpnext.controllers.selling_controller import SellingController from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data @@ -924,7 +923,7 @@ class SalesInvoice(SellingController): asset.db_set("disposal_date", None) if asset.calculate_depreciation: - self.reverse_depreciation_entry_made_after_sale(asset) + self.reverse_depreciation_entry_made_after_disposal(asset) self.reset_depreciation_schedule(asset) else: @@ -980,89 +979,6 @@ class SalesInvoice(SellingController): self.check_finance_books(item, asset) return asset - def check_finance_books(self, item, asset): - if (len(asset.finance_books) > 1 and not item.finance_book - and asset.finance_books[0].finance_book): - frappe.throw(_("Select finance book for the item {0} at row {1}") - .format(item.item_code, item.idx)) - - def depreciate_asset(self, asset): - asset.flags.ignore_validate_update_after_submit = True - asset.prepare_depreciation_data(date_of_sale=self.posting_date) - asset.save() - - make_depreciation_entry(asset.name, self.posting_date) - - def reset_depreciation_schedule(self, asset): - asset.flags.ignore_validate_update_after_submit = True - - # recreate original depreciation schedule of the asset - asset.prepare_depreciation_data(date_of_return=self.posting_date) - - self.modify_depreciation_schedule_for_asset_repairs(asset) - asset.save() - - def modify_depreciation_schedule_for_asset_repairs(self, asset): - asset_repairs = frappe.get_all( - 'Asset Repair', - filters = {'asset': asset.name}, - fields = ['name', 'increase_in_asset_life'] - ) - - for repair in asset_repairs: - if repair.increase_in_asset_life: - asset_repair = frappe.get_doc('Asset Repair', repair.name) - asset_repair.modify_depreciation_schedule() - asset.prepare_depreciation_data() - - def reverse_depreciation_entry_made_after_sale(self, asset): - from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry - - posting_date_of_original_invoice = self.get_posting_date_of_sales_invoice() - - row = -1 - finance_book = asset.get('schedules')[0].get('finance_book') - for schedule in asset.get('schedules'): - if schedule.finance_book != finance_book: - row = 0 - finance_book = schedule.finance_book - else: - row += 1 - - if schedule.schedule_date == posting_date_of_original_invoice: - if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice) \ - or self.sale_happens_in_the_future(posting_date_of_original_invoice): - - reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry) - reverse_journal_entry.posting_date = nowdate() - frappe.flags.is_reverse_depr_entry = True - reverse_journal_entry.submit() - - frappe.flags.is_reverse_depr_entry = False - asset.flags.ignore_validate_update_after_submit = True - schedule.journal_entry = None - asset.save() - - def get_posting_date_of_sales_invoice(self): - return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date') - - # if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone - def sale_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_invoice): - for finance_book in asset.get('finance_books'): - if schedule.finance_book == finance_book.finance_book: - orginal_schedule_date = add_months(finance_book.depreciation_start_date, - row * cint(finance_book.frequency_of_depreciation)) - - if orginal_schedule_date == posting_date_of_original_invoice: - return True - return False - - def sale_happens_in_the_future(self, posting_date_of_original_invoice): - if posting_date_of_original_invoice > getdate(): - return True - - return False - @property def enable_discount_accounting(self): if not hasattr(self, "_enable_discount_accounting"): diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index cf62f496ea..7c05488db5 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -75,12 +75,12 @@ class Asset(AccountsController): if self.is_existing_asset and self.purchase_invoice: frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name)) - def prepare_depreciation_data(self, date_of_sale=None, date_of_return=None): + def prepare_depreciation_data(self, date_of_disposal=None, date_of_return=None): if self.calculate_depreciation: self.value_after_depreciation = 0 self.set_depreciation_rate() - self.make_depreciation_schedule(date_of_sale) - self.set_accumulated_depreciation(date_of_sale, date_of_return) + self.make_depreciation_schedule(date_of_disposal) + self.set_accumulated_depreciation(date_of_disposal, date_of_return) else: self.finance_books = [] self.value_after_depreciation = (flt(self.gross_purchase_amount) - @@ -181,7 +181,7 @@ class Asset(AccountsController): d.rate_of_depreciation = flt(self.get_depreciation_rate(d, on_validate=True), d.precision("rate_of_depreciation")) - def make_depreciation_schedule(self, date_of_sale): + def make_depreciation_schedule(self, date_of_disposal): if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.get('schedules'): self.schedules = [] @@ -227,14 +227,14 @@ class Asset(AccountsController): monthly_schedule_date = add_months(schedule_date, - d.frequency_of_depreciation + 1) # if asset is being sold - if date_of_sale: + if date_of_disposal: from_date = self.get_from_date(d.finance_book) depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount, - from_date, date_of_sale) + from_date, date_of_disposal) if depreciation_amount > 0: self.append("schedules", { - "schedule_date": date_of_sale, + "schedule_date": date_of_disposal, "depreciation_amount": depreciation_amount, "depreciation_method": d.depreciation_method, "finance_book": d.finance_book, diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 5a2398650b..7d08581cbe 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -375,8 +375,8 @@ class AssetCapitalization(StockController): else: if asset.calculate_depreciation: self.depreciate_asset(asset) + asset.reload() - asset.reload() fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset, item.asset_value, item.get('finance_book') or self.get('finance_book')) asset.db_set("disposal_date", self.posting_date) diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py index 9bfc88b28c..5a342f7d2f 100644 --- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py @@ -118,11 +118,11 @@ class TestAssetCapitalization(unittest.TestCase): depreciation_before_disposal_amount = 15_000 accumulated_depreciation = 45_000 - # to accomodate for depreciation on disposal calculation bugs TODO remove this when bug is fixed - consumed_asset_value_before_disposal = 60_082.19 - target_incoming_rate = 6008.219 - depreciation_before_disposal_amount = 9917.81 - accumulated_depreciation = 39_917.81 + # to accomodate for depreciation on disposal calculation minor difference + consumed_asset_value_before_disposal = 55_123.29 + target_incoming_rate = 5512.329 + depreciation_before_disposal_amount = 14_876.71 + accumulated_depreciation = 44_876.71 # Create assets consumed_asset = create_depreciation_asset( diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 9d19639020..6b681ffee5 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -38,7 +38,7 @@ from erpnext.accounts.party import ( validate_party_frozen_disabled, ) from erpnext.accounts.utils import get_account_currency, get_fiscal_years, validate_fiscal_year -from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries +from erpnext.assets.doctype.asset.depreciation import make_depreciation_entry from erpnext.buying.utils import update_last_purchase_rate from erpnext.controllers.print_settings import ( set_print_templates_for_item_table, @@ -1516,17 +1516,16 @@ class AccountsController(TransactionBase): def depreciate_asset(self, asset): asset.flags.ignore_validate_update_after_submit = True - asset.prepare_depreciation_data(self.posting_date) + asset.prepare_depreciation_data(date_of_disposal=self.posting_date) asset.save() - post_depreciation_entries(self.posting_date, commit=False) + make_depreciation_entry(asset.name, self.posting_date) def reset_depreciation_schedule(self, asset): asset.flags.ignore_validate_update_after_submit = True # recreate original depreciation schedule of the asset - self.delete_depreciation_entry_made_after_disposal(asset) - asset.prepare_depreciation_data() + asset.prepare_depreciation_data(date_of_return=self.posting_date) self.modify_depreciation_schedule_for_asset_repairs(asset) asset.save() @@ -1544,10 +1543,10 @@ class AccountsController(TransactionBase): asset_repair.modify_depreciation_schedule() asset.prepare_depreciation_data() - def delete_depreciation_entry_made_after_disposal(self, asset): + def reverse_depreciation_entry_made_after_disposal(self, asset): from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry - posting_date_of_original_invoice = self.get_posting_date_of_disposal_entry() + posting_date_of_original_disposal = self.get_posting_date_of_disposal_entry() row = -1 finance_book = asset.get('schedules')[0].get('finance_book') @@ -1558,19 +1557,19 @@ class AccountsController(TransactionBase): else: row += 1 - if schedule.schedule_date == posting_date_of_original_invoice: - if not self.disposal_was_made_on_original_schedule_date(asset, schedule, row, - posting_date_of_original_invoice) or getdate(schedule.schedule_date) > getdate(today()): + if schedule.schedule_date == posting_date_of_original_disposal: + if not self.disposal_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_disposal) \ + or self.disposal_happens_in_the_future(posting_date_of_original_disposal): + reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry) reverse_journal_entry.posting_date = nowdate() - - for d in reverse_journal_entry.accounts: - d.reference_type = "Asset" - d.reference_name = asset.name - + frappe.flags.is_reverse_depr_entry = True reverse_journal_entry.submit() - schedule.db_set('journal_entry', None) + frappe.flags.is_reverse_depr_entry = False + asset.flags.ignore_validate_update_after_submit = True + schedule.journal_entry = None + asset.save() def get_posting_date_of_disposal_entry(self): if self.doctype == "Sales Invoice" and self.return_against: @@ -1579,16 +1578,22 @@ class AccountsController(TransactionBase): return self.posting_date # if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone - def disposal_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_disposal): + def disposal_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_disposal): for finance_book in asset.get('finance_books'): if schedule.finance_book == finance_book.finance_book: orginal_schedule_date = add_months(finance_book.depreciation_start_date, row * cint(finance_book.frequency_of_depreciation)) - if orginal_schedule_date == posting_date_of_original_disposal: + if orginal_schedule_date == posting_date_of_disposal: return True return False + def disposal_happens_in_the_future(self, posting_date_of_disposal): + if posting_date_of_disposal > getdate(): + return True + + return False + @frappe.whitelist() def get_tax_rate(account_head): return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True) From 85d1a237ce13d857ebbecd6a7e196c06d9aa3735 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Thu, 4 Nov 2021 14:15:47 +0500 Subject: [PATCH 0016/1047] fix(Asset Capitalization): Reverse depreciation on cancel --- .../assets/doctype/asset_capitalization/asset_capitalization.py | 1 + .../doctype/asset_capitalization/test_asset_capitalization.py | 1 + 2 files changed, 2 insertions(+) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 7d08581cbe..a8f2d79c27 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -371,6 +371,7 @@ class AssetCapitalization(StockController): self.set_consumed_asset_status(asset) if asset.calculate_depreciation: + self.reverse_depreciation_entry_made_after_disposal(asset) self.reset_depreciation_schedule(asset) else: if asset.calculate_depreciation: diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py index 5a342f7d2f..7046de6f83 100644 --- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py @@ -174,6 +174,7 @@ class TestAssetCapitalization(unittest.TestCase): self.assertEqual(actual_gle, expected_gle) # Cancel Asset Capitalization and make test entries and status are reversed + asset_capitalization.reload() asset_capitalization.cancel() self.assertEqual(consumed_asset.db_get('status'), 'Partially Depreciated') self.assertFalse(get_actual_gle_dict(asset_capitalization.name)) From cdb18000871931def61dda3f1b4a45b4c38d444b Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Tue, 9 Nov 2021 12:35:01 +0500 Subject: [PATCH 0017/1047] chore: remove unused import --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 86b7aaac87..e83873fe6c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -9,7 +9,6 @@ from frappe.model.mapper import get_mapped_doc from frappe.model.utils import get_fetch_values from frappe.utils import ( add_days, - add_months, cint, cstr, flt, From 06aead0470d0c1659e7c15db642cb9a10f2999f6 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Wed, 10 Nov 2021 13:45:40 +0500 Subject: [PATCH 0018/1047] chore: isort --- .../accounts/doctype/sales_invoice/sales_invoice.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index e83873fe6c..5297cc9502 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -7,16 +7,7 @@ from frappe import _, msgprint, throw from frappe.contacts.doctype.address.address import get_address_display from frappe.model.mapper import get_mapped_doc from frappe.model.utils import get_fetch_values -from frappe.utils import ( - add_days, - cint, - cstr, - flt, - formatdate, - get_link_to_form, - getdate, - nowdate, -) +from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate import erpnext from erpnext.accounts.deferred_revenue import validate_service_stop_date From 0e7c4314b40b98efc144f2f5491fde2e16c40d46 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 1 Aug 2022 14:03:12 +0530 Subject: [PATCH 0019/1047] fix: minor changed link --- .../doctype/request_for_quotation/request_for_quotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f319506ff9..3ef57bb70f 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -112,7 +112,7 @@ class RequestforQuotation(BuyingController): def get_link(self): # RFQ link for supplier portal - return get_url("/rfq/" + self.name) + return get_url("/app/request-for-quotation/" + self.name) def update_supplier_part_no(self, supplier): self.vendor = supplier From 0ef9c03f0560803e6b95bf623907a50837937622 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 2 Aug 2022 16:13:51 +0530 Subject: [PATCH 0020/1047] chore: CODEOWNERS [skip ci] --- CODEOWNERS | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index ecbae86d96..b6aadb3f2e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -11,22 +11,18 @@ erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar erpnext/support/ @nextchamp-saqib @deepeshgarg007 pos* @nextchamp-saqib -erpnext/buying/ @marination @rohitwaghchaure @s-aga-r -erpnext/e_commerce/ @marination -erpnext/maintenance/ @marination @rohitwaghchaure @s-aga-r -erpnext/manufacturing/ @marination @rohitwaghchaure @s-aga-r -erpnext/portal/ @marination +erpnext/buying/ @rohitwaghchaure @s-aga-r +erpnext/maintenance/ @rohitwaghchaure @s-aga-r +erpnext/manufacturing/ @rohitwaghchaure @s-aga-r erpnext/quality_management/ @marination @rohitwaghchaure @s-aga-r -erpnext/shopping_cart/ @marination erpnext/stock/ @marination @rohitwaghchaure @s-aga-r -erpnext/crm/ @NagariaHussain -erpnext/education/ @rutwikhdev +erpnext/crm/ @NagariaHussain +erpnext/education/ @rutwikhdev erpnext/projects/ @ruchamahabal -erpnext/controllers/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination -erpnext/patches/ @deepeshgarg007 @nextchamp-saqib @marination -erpnext/public/ @nextchamp-saqib @marination +erpnext/controllers/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure +erpnext/patches/ @deepeshgarg007 @nextchamp-saqib .github/ @ankush -pyproject.toml @gavindsouza @ankush +pyproject.toml @ankush From 9baa2229761c5415f29646a1a5bed4a3f4981e05 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 3 Aug 2022 05:42:30 +0000 Subject: [PATCH 0021/1047] fix: specify allowed doctype in queries (#31761) --- erpnext/controllers/queries.py | 40 +++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 243ebb66e2..4f8b5c79d2 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -18,8 +18,9 @@ from erpnext.stock.get_item_details import _get_item_tax_template @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def employee_query(doctype, txt, searchfield, start, page_len, filters): + doctype = "Employee" conditions = [] - fields = get_fields("Employee", ["name", "employee_name"]) + fields = get_fields(doctype, ["name", "employee_name"]) return frappe.db.sql( """select {fields} from `tabEmployee` @@ -49,7 +50,8 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def lead_query(doctype, txt, searchfield, start, page_len, filters): - fields = get_fields("Lead", ["name", "lead_name", "company_name"]) + doctype = "Lead" + fields = get_fields(doctype, ["name", "lead_name", "company_name"]) return frappe.db.sql( """select {fields} from `tabLead` @@ -77,6 +79,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def customer_query(doctype, txt, searchfield, start, page_len, filters): + doctype = "Customer" conditions = [] cust_master_name = frappe.defaults.get_user_default("cust_master_name") @@ -85,9 +88,9 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): else: fields = ["name", "customer_name", "customer_group", "territory"] - fields = get_fields("Customer", fields) + fields = get_fields(doctype, fields) - searchfields = frappe.get_meta("Customer").get_search_fields() + searchfields = frappe.get_meta(doctype).get_search_fields() searchfields = " or ".join(field + " like %(txt)s" for field in searchfields) return frappe.db.sql( @@ -116,6 +119,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def supplier_query(doctype, txt, searchfield, start, page_len, filters): + doctype = "Supplier" supp_master_name = frappe.defaults.get_user_default("supp_master_name") if supp_master_name == "Supplier Name": @@ -123,7 +127,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): else: fields = ["name", "supplier_name", "supplier_group"] - fields = get_fields("Supplier", fields) + fields = get_fields(doctype, fields) return frappe.db.sql( """select {field} from `tabSupplier` @@ -147,6 +151,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def tax_account_query(doctype, txt, searchfield, start, page_len, filters): + doctype = "Account" company_currency = erpnext.get_company_currency(filters.get("company")) def get_accounts(with_account_type_filter): @@ -197,13 +202,14 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): + doctype = "Item" 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) + meta = frappe.get_meta(doctype, cached=True) searchfields = meta.get_search_fields() # these are handled separately @@ -257,7 +263,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals filters.pop("supplier", None) description_cond = "" - if frappe.db.count("Item", cache=True) < 50000: + if frappe.db.count(doctype, cache=True) < 50000: # scan description only if items are less than 50000 description_cond = "or tabItem.description LIKE %(txt)s" return frappe.db.sql( @@ -300,8 +306,9 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def bom(doctype, txt, searchfield, start, page_len, filters): + doctype = "BOM" conditions = [] - fields = get_fields("BOM", ["name", "item"]) + fields = get_fields(doctype, ["name", "item"]) return frappe.db.sql( """select {fields} @@ -331,6 +338,7 @@ def bom(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_project_name(doctype, txt, searchfield, start, page_len, filters): + doctype = "Project" cond = "" if filters and filters.get("customer"): cond = """(`tabProject`.customer = %s or @@ -338,8 +346,8 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): frappe.db.escape(filters.get("customer")) ) - fields = get_fields("Project", ["name", "project_name"]) - searchfields = frappe.get_meta("Project").get_search_fields() + fields = get_fields(doctype, ["name", "project_name"]) + searchfields = frappe.get_meta(doctype).get_search_fields() searchfields = " or ".join(["`tabProject`." + field + " like %(txt)s" for field in searchfields]) return frappe.db.sql( @@ -366,7 +374,8 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict): - fields = get_fields("Delivery Note", ["name", "customer", "posting_date"]) + doctype = "Delivery Note" + fields = get_fields(doctype, ["name", "customer", "posting_date"]) return frappe.db.sql( """ @@ -402,6 +411,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_batch_no(doctype, txt, searchfield, start, page_len, filters): + doctype = "Batch" cond = "" if filters.get("posting_date"): cond = "and (batch.expiry_date is null or batch.expiry_date >= %(posting_date)s)" @@ -420,7 +430,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): if filters.get("is_return"): having_clause = "" - meta = frappe.get_meta("Batch", cached=True) + meta = frappe.get_meta(doctype, cached=True) searchfields = meta.get_search_fields() search_columns = "" @@ -496,6 +506,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_account_list(doctype, txt, searchfield, start, page_len, filters): + doctype = "Account" filter_list = [] if isinstance(filters, dict): @@ -514,7 +525,7 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters): filter_list.append([doctype, searchfield, "like", "%%%s%%" % txt]) return frappe.desk.reportview.execute( - "Account", + doctype, filters=filter_list, fields=["name", "parent_account"], limit_start=start, @@ -553,6 +564,7 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters): if not filters: filters = {} + doctype = "Account" condition = "" if filters.get("company"): condition += "and tabAccount.company = %(company)s" @@ -628,6 +640,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters): if not filters: filters = {} + doctype = "Account" condition = "" if filters.get("company"): condition += "and tabAccount.company = %(company)s" @@ -650,6 +663,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters): @frappe.validate_and_sanitize_search_inputs def warehouse_query(doctype, txt, searchfield, start, page_len, filters): # Should be used when item code is passed in filters. + doctype = "Warehouse" conditions, bin_conditions = [], [] filter_dict = get_doctype_wise_filters(filters) From 9c580dde399d12a325b5c0b574f66a59f84dd2ac Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Wed, 3 Aug 2022 01:46:59 -0400 Subject: [PATCH 0022/1047] fix: linter (#31763) --- .../accounts/report/accounts_receivable/accounts_receivable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index c7c746bede..e937edbeb2 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -165,7 +165,7 @@ class ReceivablePayableReport(object): "range4", "range5", "future_amount", - "remaining_balance" + "remaining_balance", ] def get_voucher_balance(self, ple): From ea88451875e802a47d3e46e4dbcca33c1278662c Mon Sep 17 00:00:00 2001 From: HarryPaulo Date: Wed, 3 Aug 2022 02:51:30 -0300 Subject: [PATCH 0023/1047] =?UTF-8?q?fix:=20getting=20error=20to=20show=20?= =?UTF-8?q?sales=20invoice=20group=20or=20print=20rep=E2=80=A6=20(#31756)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: formatter getting error to show sales invoice group or print report. 1 - When I view the Gross Profit report in Sales Invoice mode, the table is all broken. Error on browser console: TypeError: Cannot read properties of undefined (reading 'indent') 2 - When I try to print, no matter the Group (Sales Invoice, Item Code, Item Group...) nothing happens. in browser log console I have the following error: TypeError: Cannot read properties of undefined (reading 'content') i fixed both errors and all working perfectly. --- erpnext/accounts/report/gross_profit/gross_profit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js index 3d37b5898c..21205c3163 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.js +++ b/erpnext/accounts/report/gross_profit/gross_profit.js @@ -44,14 +44,14 @@ frappe.query_reports["Gross Profit"] = { "parent_field": "parent_invoice", "initial_depth": 3, "formatter": function(value, row, column, data, default_formatter) { - if (column.fieldname == "sales_invoice" && column.options == "Item" && data.indent == 0) { + if (column.fieldname == "sales_invoice" && column.options == "Item" && data && data.indent == 0) { column._options = "Sales Invoice"; } else { column._options = "Item"; } value = default_formatter(value, row, column, data); - if (data && (data.indent == 0.0 || row[1].content == "Total")) { + if (data && (data.indent == 0.0 || (row[1] && row[1].content == "Total"))) { value = $(`${value}`); var $value = $(value).css("font-weight", "bold"); value = $value.wrap("

").parent().html(); From 17b9bfd2497e6dc4b1179bd1eb9584e526606e2a Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 3 Aug 2022 16:48:27 +0530 Subject: [PATCH 0024/1047] fix(ecommerce): remove query to non-existing field (#31771) --- erpnext/setup/doctype/item_group/item_group.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 6e940d0cfd..411176b70a 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -142,10 +142,6 @@ def get_item_for_list_in_html(context): if (context.get("website_image") or "").startswith("files/"): context["website_image"] = "/" + quote(context["website_image"]) - context["show_availability_status"] = cint( - frappe.db.get_single_value("E Commerce Settings", "show_availability_status") - ) - products_template = "templates/includes/products_as_list.html" return frappe.get_template(products_template).render(context) From 91762097a54bfcbb5d693a3feb4238b0ae28cf78 Mon Sep 17 00:00:00 2001 From: hrzzz Date: Wed, 3 Aug 2022 13:09:23 -0300 Subject: [PATCH 0025/1047] fix: for Tree Type item and item group show net amout --- erpnext/selling/report/sales_analytics/sales_analytics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 9d7d806c71..186352848d 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -168,7 +168,7 @@ class Analytics(object): def get_sales_transactions_based_on_items(self): if self.filters["value_quantity"] == "Value": - value_field = "base_amount" + value_field = "base_net_amount" else: value_field = "stock_qty" @@ -216,7 +216,7 @@ class Analytics(object): def get_sales_transactions_based_on_item_group(self): if self.filters["value_quantity"] == "Value": - value_field = "base_amount" + value_field = "base_net_amount" else: value_field = "qty" From 5f1562c5b21da32e7f963a8b052e0eb26b7e7684 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 28 Jul 2022 11:57:27 +0530 Subject: [PATCH 0026/1047] fix: posting_date of linked vouchers should not affect outstanding posting_date filter should not be applied for linked vouchers. --- .../accounts/doctype/payment_entry/payment_entry.py | 4 +++- .../payment_reconciliation.py | 12 ++++++++---- erpnext/accounts/utils.py | 13 ++++++++++++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index af5a5e249d..48edda9032 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1184,6 +1184,7 @@ def get_outstanding_reference_documents(args): ple = qb.DocType("Payment Ledger Entry") common_filter = [] + posting_and_due_date = [] # confirm that Supplier is not blocked if args.get("party_type") == "Supplier": @@ -1224,7 +1225,7 @@ def get_outstanding_reference_documents(args): condition += " and {0} between '{1}' and '{2}'".format( fieldname, args.get(date_fields[0]), args.get(date_fields[1]) ) - common_filter.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])]) + posting_and_due_date.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])]) if args.get("company"): condition += " and company = {0}".format(frappe.db.escape(args.get("company"))) @@ -1235,6 +1236,7 @@ def get_outstanding_reference_documents(args): args.get("party"), args.get("party_account"), common_filter=common_filter, + posting_date=posting_and_due_date, min_outstanding=args.get("outstanding_amt_greater_than"), max_outstanding=args.get("outstanding_amt_less_than"), ) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 5ed34d34a3..601fc87a22 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -22,6 +22,7 @@ class PaymentReconciliation(Document): def __init__(self, *args, **kwargs): super(PaymentReconciliation, self).__init__(*args, **kwargs) self.common_filter_conditions = [] + self.ple_posting_date_filter = [] @frappe.whitelist() def get_unreconciled_entries(self): @@ -150,6 +151,7 @@ class PaymentReconciliation(Document): return_outstanding = ple_query.get_voucher_outstandings( vouchers=return_invoices, common_filter=self.common_filter_conditions, + posting_date=self.ple_posting_date_filter, min_outstanding=-(self.minimum_payment_amount) if self.minimum_payment_amount else None, max_outstanding=-(self.maximum_payment_amount) if self.maximum_payment_amount else None, get_payments=True, @@ -187,6 +189,7 @@ class PaymentReconciliation(Document): self.party, self.receivable_payable_account, common_filter=self.common_filter_conditions, + posting_date=self.ple_posting_date_filter, min_outstanding=self.minimum_invoice_amount if self.minimum_invoice_amount else None, max_outstanding=self.maximum_invoice_amount if self.maximum_invoice_amount else None, ) @@ -350,6 +353,7 @@ class PaymentReconciliation(Document): def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False): self.common_filter_conditions.clear() + self.ple_posting_date_filter.clear() ple = qb.DocType("Payment Ledger Entry") self.common_filter_conditions.append(ple.company == self.company) @@ -359,15 +363,15 @@ class PaymentReconciliation(Document): if get_invoices: if self.from_invoice_date: - self.common_filter_conditions.append(ple.posting_date.gte(self.from_invoice_date)) + self.ple_posting_date_filter.append(ple.posting_date.gte(self.from_invoice_date)) if self.to_invoice_date: - self.common_filter_conditions.append(ple.posting_date.lte(self.to_invoice_date)) + self.ple_posting_date_filter.append(ple.posting_date.lte(self.to_invoice_date)) elif get_return_invoices: if self.from_payment_date: - self.common_filter_conditions.append(ple.posting_date.gte(self.from_payment_date)) + self.ple_posting_date_filter.append(ple.posting_date.gte(self.from_payment_date)) if self.to_payment_date: - self.common_filter_conditions.append(ple.posting_date.lte(self.to_payment_date)) + self.ple_posting_date_filter.append(ple.posting_date.lte(self.to_payment_date)) def get_conditions(self, get_payments=False): condition = " and company = '{0}' ".format(self.company) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 9dafef74f4..018e8f9301 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -823,7 +823,13 @@ def get_held_invoices(party_type, party): def get_outstanding_invoices( - party_type, party, account, common_filter=None, min_outstanding=None, max_outstanding=None + party_type, + party, + account, + common_filter=None, + posting_date=None, + min_outstanding=None, + max_outstanding=None, ): ple = qb.DocType("Payment Ledger Entry") @@ -850,6 +856,7 @@ def get_outstanding_invoices( ple_query = QueryPaymentLedger() invoice_list = ple_query.get_voucher_outstandings( common_filter=common_filter, + posting_date=posting_date, min_outstanding=min_outstanding, max_outstanding=max_outstanding, get_invoices=True, @@ -1501,6 +1508,7 @@ class QueryPaymentLedger(object): # query filters self.vouchers = [] self.common_filter = [] + self.voucher_posting_date = [] self.min_outstanding = None self.max_outstanding = None @@ -1571,6 +1579,7 @@ class QueryPaymentLedger(object): .where(ple.delinked == 0) .where(Criterion.all(filter_on_voucher_no)) .where(Criterion.all(self.common_filter)) + .where(Criterion.all(self.voucher_posting_date)) .groupby(ple.voucher_type, ple.voucher_no, ple.party_type, ple.party) ) @@ -1652,6 +1661,7 @@ class QueryPaymentLedger(object): self, vouchers=None, common_filter=None, + posting_date=None, min_outstanding=None, max_outstanding=None, get_payments=False, @@ -1671,6 +1681,7 @@ class QueryPaymentLedger(object): self.reset() self.vouchers = vouchers self.common_filter = common_filter or [] + self.voucher_posting_date = posting_date or [] self.min_outstanding = min_outstanding self.max_outstanding = max_outstanding self.get_payments = get_payments From ef312b8fc46355197a9a9502dd2ade697635fe77 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 29 Jul 2022 15:05:01 +0530 Subject: [PATCH 0027/1047] test: posting_date should not affect outstanding amount calculation --- .../test_payment_reconciliation.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 625382a3e9..dae029b408 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -283,6 +283,41 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(len(pr.get("invoices")), 2) self.assertEqual(len(pr.get("payments")), 2) + def test_filter_posting_date_case2(self): + """ + Posting date should not affect outstanding amount calculation + """ + + from_date = add_days(nowdate(), -30) + to_date = nowdate() + self.create_payment_entry(amount=25, posting_date=from_date).submit() + self.create_sales_invoice(rate=25, qty=1, posting_date=to_date) + + pr = self.create_payment_reconciliation() + pr.from_invoice_date = pr.from_payment_date = from_date + pr.to_invoice_date = pr.to_payment_date = to_date + pr.get_unreconciled_entries() + + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 1) + + invoices = [x.as_dict() for x in pr.invoices] + payments = [x.as_dict() for x in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + + pr.get_unreconciled_entries() + + self.assertEqual(len(pr.invoices), 0) + self.assertEqual(len(pr.payments), 0) + + pr.from_invoice_date = pr.from_payment_date = to_date + pr.to_invoice_date = pr.to_payment_date = to_date + + pr.get_unreconciled_entries() + + self.assertEqual(len(pr.invoices), 0) + def test_filter_invoice_limit(self): # check filter condition - invoice limit transaction_date = nowdate() From af0a353b79ed3b6a079c142be69da9cc75df2929 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 4 Aug 2022 09:48:48 +0530 Subject: [PATCH 0028/1047] fix: intercompany SO throws exception --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index f8c26d1e92..19a234d9df 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -2103,13 +2103,13 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): target_detail_field = "sales_invoice_item" if doctype == "Sales Invoice" else "sales_order_item" source_document_warehouse_field = "target_warehouse" target_document_warehouse_field = "from_warehouse" + received_items = get_received_items(source_name, target_doctype, target_detail_field) else: source_doc = frappe.get_doc(doctype, source_name) target_doctype = "Sales Invoice" if doctype == "Purchase Invoice" else "Sales Order" source_document_warehouse_field = "from_warehouse" target_document_warehouse_field = "target_warehouse" - - received_items = get_received_items(source_name, target_doctype, target_detail_field) + received_items = {} validate_inter_company_transaction(source_doc, doctype) details = get_inter_company_details(source_doc, doctype) From 452584c4bd512f1eb0e80acbab5fd1dc0982fc9c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 28 Jul 2022 07:11:16 +0530 Subject: [PATCH 0029/1047] fix: add asset repair to accounting dimension list --- erpnext/hooks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index aa10e31744..c4f0c59c38 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -507,6 +507,7 @@ accounting_dimension_doctypes = [ "Shipping Rule", "Landed Cost Item", "Asset Value Adjustment", + "Asset Repair", "Loyalty Program", "Stock Reconciliation", "POS Profile", From a272d73dd9b30d014829d97c9d0a642286d7d38e Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Thu, 4 Aug 2022 19:04:34 +0530 Subject: [PATCH 0030/1047] fix: pending principal- amount --- .../loan_balance_adjustment.py | 2 +- .../loan_disbursement/loan_disbursement.json | 6 ++--- .../loan_disbursement/loan_disbursement.py | 3 +++ .../loan_interest_accrual.py | 3 +++ .../loan_repayment/loan_repayment.json | 6 ++--- .../doctype/loan_repayment/loan_repayment.py | 5 +++- .../loan_security_unpledge.py | 3 +++ .../doctype/loan_write_off/loan_write_off.py | 25 ++++++++++++++----- 8 files changed, 39 insertions(+), 14 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py index 0a576d6969..514a5fcfaf 100644 --- a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py @@ -99,7 +99,7 @@ class LoanBalanceAdjustment(AccountsController): loan_account = frappe.db.get_value("Loan", self.loan, "loan_account") remarks = "{} against loan {}".format(self.adjustment_type.capitalize(), self.loan) if self.reference_number: - remarks += "with reference no. {}".format(self.reference_number) + remarks += " with reference no. {}".format(self.reference_number) loan_entry = { "account": loan_account, diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json index 50926d7726..c7b5c03375 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json @@ -163,11 +163,11 @@ }, { "fetch_from": "against_loan.disbursement_account", + "fetch_if_empty": 1, "fieldname": "disbursement_account", "fieldtype": "Link", "label": "Disbursement Account", - "options": "Account", - "read_only": 1 + "options": "Account" }, { "fieldname": "column_break_16", @@ -185,7 +185,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-02-17 18:23:44.157598", + "modified": "2022-08-04 17:16:04.922444", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Disbursement", diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py index 0c2042ba50..b73dee26b9 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py @@ -209,6 +209,9 @@ def get_disbursal_amount(loan, on_current_security_price=0): "loan_amount", "disbursed_amount", "total_payment", + "debit_adjustment_amount", + "credit_adjustment_amount", + "refund_amount", "total_principal_paid", "total_interest_payable", "status", diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 0aeb448918..4680456211 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -147,6 +147,9 @@ def make_accrual_interest_entry_for_demand_loans( "name", "total_payment", "total_amount_paid", + "debit_adjustment_amount", + "credit_adjustment_amount", + "refund_amount", "loan_account", "interest_income_account", "loan_amount", diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json index 76dc8b462e..3e7dc28f71 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json @@ -264,11 +264,11 @@ }, { "fetch_from": "against_loan.payment_account", + "fetch_if_empty": 1, "fieldname": "payment_account", "fieldtype": "Link", "label": "Repayment Account", - "options": "Account", - "read_only": 1 + "options": "Account" }, { "fieldname": "column_break_36", @@ -294,7 +294,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-06-21 10:10:07.742298", + "modified": "2022-08-04 17:13:51.964203", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Repayment", diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 07a1d0d850..29da988ce4 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -149,6 +149,9 @@ class LoanRepayment(AccountsController): "status", "is_secured_loan", "total_payment", + "debit_adjustment_amount", + "credit_adjustment_amount", + "refund_amount", "loan_amount", "disbursed_amount", "total_interest_payable", @@ -398,7 +401,7 @@ class LoanRepayment(AccountsController): remarks = "Repayment against loan " + self.against_loan if self.reference_number: - remarks += "with reference no. {}".format(self.reference_number) + remarks += " with reference no. {}".format(self.reference_number) if hasattr(self, "repay_from_salary") and self.repay_from_salary: payment_account = self.payroll_payable_account diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py index 0ab7beb0fc..15a9c4a85e 100644 --- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py +++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py @@ -57,6 +57,9 @@ class LoanSecurityUnpledge(Document): self.loan, [ "total_payment", + "debit_adjustment_amount", + "credit_adjustment_amount", + "refund_amount", "total_principal_paid", "loan_amount", "total_interest_payable", diff --git a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py index 25aecf673b..ae483f9759 100644 --- a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py +++ b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py @@ -9,6 +9,9 @@ from frappe.utils import cint, flt, getdate import erpnext from erpnext.accounts.general_ledger import make_gl_entries from erpnext.controllers.accounts_controller import AccountsController +from erpnext.loan_management.doctype.loan_repayment.loan_repayment import ( + get_pending_principal_amount, +) class LoanWriteOff(AccountsController): @@ -22,16 +25,26 @@ class LoanWriteOff(AccountsController): def validate_write_off_amount(self): precision = cint(frappe.db.get_default("currency_precision")) or 2 - total_payment, principal_paid, interest_payable, written_off_amount = frappe.get_value( + + loan_details = frappe.get_value( "Loan", self.loan, - ["total_payment", "total_principal_paid", "total_interest_payable", "written_off_amount"], + [ + "total_payment", + "debit_adjustment_amount", + "credit_adjustment_amount", + "refund_amount", + "total_principal_paid", + "loan_amount", + "total_interest_payable", + "written_off_amount", + "disbursed_amount", + "status", + ], + as_dict=1, ) - pending_principal_amount = flt( - flt(total_payment) - flt(interest_payable) - flt(principal_paid) - flt(written_off_amount), - precision, - ) + pending_principal_amount = flt(get_pending_principal_amount(loan_details), precision) if self.write_off_amount > pending_principal_amount: frappe.throw(_("Write off amount cannot be greater than pending principal amount")) From 80f508c4b12ddfead304674a7eea0f8a7cadbc19 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 4 Aug 2022 16:48:03 +0530 Subject: [PATCH 0031/1047] chore: patch for creating existing dimensions in asset repair --- erpnext/patches.txt | 1 + ..._accounting_dimensions_for_asset_repair.py | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 erpnext/patches/v13_0/create_accounting_dimensions_for_asset_repair.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1d5f5d7f99..c7dc27e294 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -268,6 +268,7 @@ erpnext.patches.v13_0.enable_ksa_vat_docs #1 erpnext.patches.v13_0.show_india_localisation_deprecation_warning erpnext.patches.v13_0.show_hr_payroll_deprecation_warning erpnext.patches.v13_0.reset_corrupt_defaults +erpnext.patches.v13_0.create_accounting_dimensions_for_asset_repair [post_model_sync] execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings') diff --git a/erpnext/patches/v13_0/create_accounting_dimensions_for_asset_repair.py b/erpnext/patches/v13_0/create_accounting_dimensions_for_asset_repair.py new file mode 100644 index 0000000000..61a5c86386 --- /dev/null +++ b/erpnext/patches/v13_0/create_accounting_dimensions_for_asset_repair.py @@ -0,0 +1,29 @@ +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_field + + +def execute(): + accounting_dimensions = frappe.db.get_all( + "Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"] + ) + + if not accounting_dimensions: + return + + for d in accounting_dimensions: + doctype = "Asset Repair" + field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname}) + + if field: + continue + + df = { + "fieldname": d.fieldname, + "label": d.label, + "fieldtype": "Link", + "options": d.document_type, + "insert_after": "accounting_dimensions_section", + } + + create_custom_field(doctype, df, ignore_validate=True) + frappe.clear_cache(doctype=doctype) From a3625b3817cdadb2db0ff718ca4740e8753d6611 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 29 Jul 2022 12:41:40 +0530 Subject: [PATCH 0032/1047] fix: set `billing_address` for purchases in `get_party_details` --- erpnext/accounts/party.py | 29 ++++++++++++++++---- erpnext/controllers/buying_controller.py | 1 + erpnext/public/js/controllers/transaction.js | 19 ------------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index e39f22b4cf..67cf644353 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -207,7 +207,7 @@ def set_address_details( ) if company_address: - party_details.update({"company_address": company_address}) + party_details.company_address = company_address else: party_details.update(get_company_address(company)) @@ -219,12 +219,31 @@ def set_address_details( get_regional_address_details(party_details, doctype, company) elif doctype and doctype in ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]: - if party_details.company_address: - party_details["shipping_address"] = shipping_address or party_details["company_address"] - party_details.shipping_address_display = get_address_display(party_details["shipping_address"]) + if shipping_address: party_details.update( - get_fetch_values(doctype, "shipping_address", party_details.shipping_address) + shipping_address=shipping_address, + shipping_address_display=get_address_display(shipping_address), + **get_fetch_values(doctype, "shipping_address", shipping_address) ) + + if party_details.company_address: + # billing address + party_details.update( + billing_address=party_details.company_address, + billing_address_display=( + party_details.company_address_display or get_address_display(party_details.company_address) + ), + **get_fetch_values(doctype, "billing_address", party_details.company_address) + ) + + # shipping address - if not already set + if not party_details.shipping_address: + party_details.update( + shipping_address=party_details.billing_address, + shipping_address_display=party_details.billing_address_display, + **get_fetch_values(doctype, "shipping_address", party_details.billing_address) + ) + get_regional_address_details(party_details, doctype, company) return party_details.get(billing_address_field), party_details.shipping_address_name diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 036733c0c3..c0f37455a0 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -86,6 +86,7 @@ class BuyingController(SubcontractingController): company=self.company, party_address=self.get("supplier_address"), shipping_address=self.get("shipping_address"), + company_address=self.get("billing_address"), fetch_payment_terms_template=not self.get("ignore_default_payment_terms_template"), ignore_permissions=self.flags.ignore_permissions, ) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 85485fc7a6..c0a8c9e088 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1,7 +1,6 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.provide('erpnext.accounts.dimensions'); erpnext.TransactionController = class TransactionController extends erpnext.taxes_and_totals { setup() { @@ -794,24 +793,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe set_party_account(set_pricing); }); - // Get default company billing address in Purchase Invoice, Order and Receipt - if (this.frm.doc.company && frappe.meta.get_docfield(this.frm.doctype, "billing_address")) { - frappe.call({ - method: "erpnext.setup.doctype.company.company.get_default_company_address", - args: {name: this.frm.doc.company, existing_address: this.frm.doc.billing_address || ""}, - debounce: 2000, - callback: function(r) { - if (r.message) { - me.frm.set_value("billing_address", r.message); - } else { - if (frappe.meta.get_docfield(me.frm.doctype, 'company_address')) { - me.frm.set_value("company_address", ""); - } - } - } - }); - } - } else { set_party_account(set_pricing); } From d05082987f47d9f3582447524b314862951507e0 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 29 Jul 2022 13:20:34 +0530 Subject: [PATCH 0033/1047] fix: set `company_address` for purchases in `party.js` --- erpnext/public/js/utils/party.js | 64 ++++++++++++++------------------ 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index a492b32a9f..58594b0a13 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -3,25 +3,14 @@ frappe.provide("erpnext.utils"); +const SALES_DOCTYPES = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']; +const PURCHASE_DOCTYPES = ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']; + erpnext.utils.get_party_details = function(frm, method, args, callback) { if (!method) { method = "erpnext.accounts.party.get_party_details"; } - if (args) { - if (in_list(['Sales Invoice', 'Sales Order', 'Delivery Note'], frm.doc.doctype)) { - if (frm.doc.company_address && (!args.company_address)) { - args.company_address = frm.doc.company_address; - } - } - - if (in_list(['Purchase Invoice', 'Purchase Order', 'Purchase Receipt'], frm.doc.doctype)) { - if (frm.doc.shipping_address && (!args.shipping_address)) { - args.shipping_address = frm.doc.shipping_address; - } - } - } - if (!args) { if ((frm.doctype != "Purchase Order" && frm.doc.customer) || (frm.doc.party_name && in_list(['Quotation', 'Opportunity'], frm.doc.doctype))) { @@ -45,41 +34,44 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { }; } - if (in_list(['Sales Invoice', 'Sales Order', 'Delivery Note'], frm.doc.doctype)) { - if (!args) { + if (!args) { + if (in_list(SALES_DOCTYPES, frm.doc.doctype)) { args = { party: frm.doc.customer || frm.doc.party_name, party_type: 'Customer' - } - } - if (frm.doc.company_address && (!args.company_address)) { - args.company_address = frm.doc.company_address; + }; } - if (frm.doc.shipping_address_name &&(!args.shipping_address_name)) { - args.shipping_address_name = frm.doc.shipping_address_name; - } - } - - if (in_list(['Purchase Invoice', 'Purchase Order', 'Purchase Receipt'], frm.doc.doctype)) { - if (!args) { + if (in_list(PURCHASE_DOCTYPES, frm.doc.doctype)) { args = { party: frm.doc.supplier, party_type: 'Supplier' - } - } - - if (frm.doc.shipping_address && (!args.shipping_address)) { - args.shipping_address = frm.doc.shipping_address; + }; } } - if (args) { - args.posting_date = frm.doc.posting_date || frm.doc.transaction_date; - args.fetch_payment_terms_template = cint(!frm.doc.ignore_default_payment_terms_template); + if (!args || !args.party) return; + + args.posting_date = frm.doc.posting_date || frm.doc.transaction_date; + args.fetch_payment_terms_template = cint(!frm.doc.ignore_default_payment_terms_template); + } + + if (in_list(SALES_DOCTYPES, frm.doc.doctype)) { + if (!args.company_address && frm.doc.company_address) { + args.company_address = frm.doc.company_address; } } - if (!args || !args.party) return; + + if (in_list(PURCHASE_DOCTYPES, frm.doc.doctype)) { + if (!args.company_address && frm.doc.billing_address) { + args.company_address = frm.doc.billing_address; + } + + if (!args.shipping_address && frm.doc.shipping_address) { + args.shipping_address = frm.doc.shipping_address; + } + } + if (frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { if (!erpnext.utils.validate_mandatory(frm, "Posting / Transaction Date", From 9ef8d5c5c314ec03f46e982ce8aa36487dbb7559 Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Mon, 8 Aug 2022 16:29:13 +0530 Subject: [PATCH 0034/1047] fix: process loan interest accrual --- .../doctype/loan_interest_accrual/loan_interest_accrual.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 4680456211..9a6bcd4daa 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -135,7 +135,11 @@ def calculate_accrual_amount_for_demand_loans( def make_accrual_interest_entry_for_demand_loans( posting_date, process_loan_interest, open_loans=None, loan_type=None, accrual_type="Regular" ): - query_filters = {"status": ("in", ["Disbursed", "Partially Disbursed"]), "docstatus": 1} + query_filters = { + "status": ("in", ["Disbursed", "Partially Disbursed"]), + "docstatus": 1, + "is_term_loan": 0, + } if loan_type: query_filters.update({"loan_type": loan_type}) From 534d7ce64b47c6c9272f7bedf79f4cdce825e0f2 Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Mon, 8 Aug 2022 17:35:31 +0530 Subject: [PATCH 0035/1047] fix: term loan interest calculation --- .../doctype/loan_interest_accrual/loan_interest_accrual.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 9a6bcd4daa..6d62aefdca 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -236,6 +236,7 @@ def get_term_loans(date, term_loan=None, loan_type=None): AND l.is_term_loan =1 AND rs.payment_date <= %s AND rs.is_accrued=0 {0} + AND rs.interest_amount > 0 AND l.status = 'Disbursed' ORDER BY rs.payment_date""".format( condition From ddd24ea8c8c6f2094c71c607196dd4c91ae6c6a5 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 9 Aug 2022 14:50:20 +0530 Subject: [PATCH 0036/1047] fix: incorrect incoming rate set for inter transfer purchase receipt --- erpnext/controllers/buying_controller.py | 3 ++- erpnext/stock/stock_ledger.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 036733c0c3..69037547f4 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -307,7 +307,8 @@ class BuyingController(SubcontractingController): rate = flt(outgoing_rate * (d.conversion_factor or 1), d.precision("rate")) else: - rate = frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), "rate") + field = "incoming_rate" if self.get("is_internal_supplier") else "rate" + rate = frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), field) if self.is_internal_transfer(): if rate != d.rate: diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index fd1aece7b1..3524a47c71 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -646,6 +646,24 @@ class update_entries_after(object): voucher_detail_no=sle.voucher_detail_no, sle=sle, ) + + elif ( + sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] + and sle.actual_qty > 0 + and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier") + ): + sle_details = frappe.db.get_value( + "Stock Ledger Entry", + { + "voucher_type": sle.voucher_type, + "voucher_no": sle.voucher_no, + "dependant_sle_voucher_detail_no": sle.voucher_detail_no, + }, + ["stock_value_difference", "actual_qty"], + as_dict=1, + ) + + rate = abs(sle_details.stock_value_difference / sle.actual_qty) else: if sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"): rate_field = "valuation_rate" From 6b510546ae52ee62da359f53df8204de0b160e34 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Tue, 9 Aug 2022 11:41:52 +0200 Subject: [PATCH 0037/1047] fix: german translations (#31732) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 2 +- erpnext/translations/de.csv | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index ba4cdd606e..adcbb83e8d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -1024,7 +1024,7 @@ var select_loyalty_program = function(frm, loyalty_programs) { ] }); - dialog.set_primary_action(__("Set"), function() { + dialog.set_primary_action(__("Set Loyalty Program"), function() { dialog.hide(); return frappe.call({ method: "frappe.client.set_value", diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 01795af23e..d6bceb342d 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -4051,7 +4051,7 @@ Server Error,Serverfehler, Service Level Agreement has been changed to {0}.,Service Level Agreement wurde in {0} geändert., Service Level Agreement was reset.,Service Level Agreement wurde zurückgesetzt., Service Level Agreement with Entity Type {0} and Entity {1} already exists.,Service Level Agreement mit Entitätstyp {0} und Entität {1} ist bereits vorhanden., -Set,Menge, +Set Loyalty Program,Treueprogramm eintragen, Set Meta Tags,Festlegen von Meta-Tags, Set {0} in company {1},{0} in Firma {1} festlegen, Setup,Einstellungen, @@ -4231,10 +4231,8 @@ To date cannot be before From date,Bis-Datum kann nicht vor Von-Datum liegen, Write Off,Abschreiben, {0} Created,{0} Erstellt, Email Id,E-Mail-ID, -No,Kein, Reference Doctype,Referenz-DocType, User Id,Benutzeridentifikation, -Yes,Ja, Actual ,Tatsächlich, Add to cart,In den Warenkorb legen, Budget,Budget, From a2252c92367065559bd9bbbbb4db45ee692031d0 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Tue, 9 Aug 2022 11:49:48 +0000 Subject: [PATCH 0038/1047] ci: ensure unique group ID to prevent workflows from getting cancelled (#31806) ci: ensure unique group ID to prevent workflows from cancelling --- .github/workflows/patch.yml | 2 +- .github/workflows/server-tests-mariadb.yml | 2 +- .github/workflows/server-tests-postgres.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml index 4d2dc58677..9e06254bac 100644 --- a/.github/workflows/patch.yml +++ b/.github/workflows/patch.yml @@ -11,7 +11,7 @@ on: workflow_dispatch: concurrency: - group: patch-develop-${{ github.event.number }} + group: patch-develop-${{ github.event_name }}-${{ github.event.number || github.event_name == 'workflow_dispatch' && github.run_id || '' }} cancel-in-progress: true jobs: diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml index 64134bc539..e3b92fd269 100644 --- a/.github/workflows/server-tests-mariadb.yml +++ b/.github/workflows/server-tests-mariadb.yml @@ -27,7 +27,7 @@ on: type: string concurrency: - group: server-mariadb-develop-${{ github.event.number }} + group: server-mariadb-develop-${{ github.event_name }}-${{ github.event.number || github.event_name == 'workflow_dispatch' && github.run_id || '' }} cancel-in-progress: true jobs: diff --git a/.github/workflows/server-tests-postgres.yml b/.github/workflows/server-tests-postgres.yml index 651c935c15..df43801478 100644 --- a/.github/workflows/server-tests-postgres.yml +++ b/.github/workflows/server-tests-postgres.yml @@ -9,7 +9,7 @@ on: types: [opened, labelled, synchronize, reopened] concurrency: - group: server-postgres-develop-${{ github.event.number }} + group: server-postgres-develop-${{ github.event_name }}-${{ github.event.number || github.event_name == 'workflow_dispatch' && github.run_id || '' }} cancel-in-progress: true jobs: From 32b30bc5de0347a0c5614ccb7b05262e5877cbc1 Mon Sep 17 00:00:00 2001 From: Akash Krishna Date: Tue, 9 Aug 2022 17:41:02 +0530 Subject: [PATCH 0039/1047] Tds report (#31801) * fix: TDS Computation Summary Report not loading, too many values to unpack --- .../report/tds_computation_summary/tds_computation_summary.py | 4 ++-- .../report/tds_payable_monthly/tds_payable_monthly.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) 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 08d2008682..db3d5d44a0 100644 --- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py +++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py @@ -14,9 +14,9 @@ def execute(filters=None): filters.naming_series = frappe.db.get_single_value("Buying Settings", "supp_master_name") columns = get_columns(filters) - tds_docs, tds_accounts, tax_category_map = get_tds_docs(filters) + tds_docs, tds_accounts, tax_category_map, journal_entry_party_map = get_tds_docs(filters) - res = get_result(filters, tds_docs, tds_accounts, tax_category_map) + res = get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_party_map) final_result = group_by_supplier_and_category(res) return columns, final_result 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 16e0ac1de6..f2809a99c5 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -26,7 +26,6 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_ supplier_map = get_supplier_pan_map() tax_rate_map = get_tax_rate_map(filters) gle_map = get_gle_map(tds_docs) - print(journal_entry_party_map) out = [] for name, details in gle_map.items(): From 08d7c48dc762610360873cc516adf9ffd62430f7 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 9 Aug 2022 18:49:14 +0530 Subject: [PATCH 0040/1047] refactor: use browser native lazy loading (#31814) --- .../doctype/homepage_section/test_homepage_section.py | 6 +++++- erpnext/templates/includes/macros.html | 2 +- erpnext/templates/pages/home.html | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/portal/doctype/homepage_section/test_homepage_section.py b/erpnext/portal/doctype/homepage_section/test_homepage_section.py index 27b0169c59..27c8fe4c95 100644 --- a/erpnext/portal/doctype/homepage_section/test_homepage_section.py +++ b/erpnext/portal/doctype/homepage_section/test_homepage_section.py @@ -57,7 +57,11 @@ class TestHomepageSection(unittest.TestCase): self.assertEqual(cards[0].h5.text, "Card 1") self.assertEqual(cards[0].a["href"], "/card-1") self.assertEqual(cards[1].p.text, "Subtitle 2") - self.assertEqual(cards[1].find(class_="website-image-lazy")["data-src"], "test.jpg") + + img = cards[1].find(class_="card-img-top") + + self.assertEqual(img["src"], "test.jpg") + self.assertEqual(img["loading"], "lazy") # cleanup frappe.db.rollback() diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html index 3e20e50c3d..f56dc3a454 100644 --- a/erpnext/templates/includes/macros.html +++ b/erpnext/templates/includes/macros.html @@ -46,7 +46,7 @@
{% if card.image %} -
+ {{ card.title }} {% endif %}
{{ card.title }}
diff --git a/erpnext/templates/pages/home.html b/erpnext/templates/pages/home.html index 4c69b8388d..27d966ad42 100644 --- a/erpnext/templates/pages/home.html +++ b/erpnext/templates/pages/home.html @@ -37,7 +37,7 @@ {% for item in homepage.products %}
-
+ {{ item.item_name }}
{{ item.item_name }}
{{ _('More details') }} From 7ecd67605f660523cec6122da83dbc2515646d0c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 9 Aug 2022 19:06:57 +0530 Subject: [PATCH 0041/1047] fix: limited options for no-of-employees in crm --- erpnext/crm/doctype/lead/lead.json | 6 +++--- erpnext/crm/doctype/opportunity/opportunity.json | 4 ++-- erpnext/crm/doctype/prospect/prospect.json | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index c946ae4999..99c00ad6e6 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -340,8 +340,8 @@ "fieldname": "no_of_employees", "fieldtype": "Select", "label": "No of Employees", - "options": "1-10\n11-20\n21-30\n31-100\n11-50\n51-200\n201-500\n101-500\n500-1000\n501-1000\n>1000\n1000+" - }, + "options": "1-10\n11-50\n51-200\n201-500\n501-1000\n1000+" + }, { "fieldname": "column_break_22", "fieldtype": "Column Break" @@ -514,7 +514,7 @@ "idx": 5, "image_field": "image", "links": [], - "modified": "2022-07-22 15:55:03.176094", + "modified": "2022-08-09 18:26:17.101521", "modified_by": "Administrator", "module": "CRM", "name": "Lead", diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 68a2156981..fed0c7c79a 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -463,7 +463,7 @@ "fieldname": "no_of_employees", "fieldtype": "Select", "label": "No of Employees", - "options": "1-10\n11-20\n21-30\n31-100\n11-50\n51-200\n201-500\n101-500\n500-1000\n501-1000\n>1000\n1000+" + "options": "1-10\n11-50\n51-200\n201-500\n501-1000\n1000+" }, { "fieldname": "annual_revenue", @@ -622,7 +622,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2022-07-22 18:46:32.858696", + "modified": "2022-08-09 18:26:37.235964", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", diff --git a/erpnext/crm/doctype/prospect/prospect.json b/erpnext/crm/doctype/prospect/prospect.json index 7f33c08c13..820a6c72ea 100644 --- a/erpnext/crm/doctype/prospect/prospect.json +++ b/erpnext/crm/doctype/prospect/prospect.json @@ -82,7 +82,7 @@ "fieldname": "no_of_employees", "fieldtype": "Select", "label": "No. of Employees", - "options": "1-10\n11-20\n21-30\n31-100\n11-50\n51-200\n201-500\n101-500\n500-1000\n501-1000\n>1000\n1000+" + "options": "1-10\n11-50\n51-200\n201-500\n501-1000\n1000+" }, { "fieldname": "annual_revenue", @@ -218,7 +218,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2022-06-22 15:10:26.887502", + "modified": "2022-08-09 18:26:56.950185", "modified_by": "Administrator", "module": "CRM", "name": "Prospect", From bb40e38451b37c6d855115411cb428e3a2e39c55 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 9 Aug 2022 19:35:43 +0530 Subject: [PATCH 0042/1047] fix: limit pos recent order page result --- erpnext/selling/page/point_of_sale/point_of_sale.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 13d5069ea6..c12a9f86ae 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -253,16 +253,20 @@ def get_past_order_list(search_term, status, limit=20): "POS Invoice", filters={"customer": ["like", "%{}%".format(search_term)], "status": status}, fields=fields, + page_length=limit, ) invoices_by_name = frappe.db.get_all( "POS Invoice", filters={"name": ["like", "%{}%".format(search_term)], "status": status}, fields=fields, + page_length=limit, ) invoice_list = invoices_by_customer + invoices_by_name elif status: - invoice_list = frappe.db.get_all("POS Invoice", filters={"status": status}, fields=fields) + invoice_list = frappe.db.get_all( + "POS Invoice", filters={"status": status}, fields=fields, page_length=limit + ) return invoice_list From 909945c0ac214b3ad613ae944509e8bdec47c940 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 9 Aug 2022 19:28:26 +0530 Subject: [PATCH 0043/1047] fix: map old data as per new options of no-of-employees --- erpnext/patches.txt | 3 ++- .../patches/v14_0/fix_crm_no_of_employees.py | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v14_0/fix_crm_no_of_employees.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index c7dc27e294..e5beacde2b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -309,4 +309,5 @@ erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.patches.v14_0.crm_ux_cleanup erpnext.patches.v14_0.remove_india_localisation # 14-07-2022 erpnext.patches.v13_0.fix_number_and_frequency_for_monthly_depreciation -erpnext.patches.v14_0.remove_hr_and_payroll_modules # 20-07-2022 \ No newline at end of file +erpnext.patches.v14_0.remove_hr_and_payroll_modules # 20-07-2022 +erpnext.patches.v14_0.fix_crm_no_of_employees \ No newline at end of file diff --git a/erpnext/patches/v14_0/fix_crm_no_of_employees.py b/erpnext/patches/v14_0/fix_crm_no_of_employees.py new file mode 100644 index 0000000000..268eb95732 --- /dev/null +++ b/erpnext/patches/v14_0/fix_crm_no_of_employees.py @@ -0,0 +1,26 @@ +import frappe + + +def execute(): + options = { + "11-20": "11-50", + "21-30": "11-50", + "31-100": "51-200", + "101-500": "201-500", + "500-1000": "501-1000", + ">1000": "1000+", + } + + for doctype in ("Lead", "Opportunity", "Prospect"): + frappe.reload_doctype(doctype) + for key, value in options.items(): + frappe.db.sql( + """ + update `tab{doctype}` + set no_of_employees = %s + where no_of_employees = %s + """.format( + doctype=doctype + ), + (value, key), + ) From 33762dbbac55181371f7bcf052474d213312434b Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 10 Aug 2022 14:17:28 +0530 Subject: [PATCH 0044/1047] fix(pos): error while consolidating pos invoices --- .../accounts/doctype/pos_profile/pos_profile.json | 11 ++++++++++- erpnext/controllers/taxes_and_totals.py | 12 ++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index d5f7ee4f21..994b6776e3 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -43,6 +43,7 @@ "currency", "write_off_account", "write_off_cost_center", + "write_off_limit", "account_for_change_amount", "disable_rounded_total", "column_break_23", @@ -360,6 +361,14 @@ "fieldtype": "Check", "label": "Validate Stock on Save" }, + { + "default": "1", + "description": "Auto write off precision loss while consolidation", + "fieldname": "write_off_limit", + "fieldtype": "Currency", + "label": "Write Off Limit", + "reqd": 1 + }, { "default": "0", "description": "If enabled, the consolidated invoices will have rounded total disabled", @@ -393,7 +402,7 @@ "link_fieldname": "pos_profile" } ], - "modified": "2022-07-21 11:16:46.911173", + "modified": "2022-08-10 12:57:06.241439", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 0d8cffe03f..26b8db4a81 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -767,6 +767,18 @@ class calculate_taxes_and_totals(object): self.doc.precision("outstanding_amount"), ) + if ( + self.doc.doctype == "Sales Invoice" + and self.doc.get("is_pos") + and self.doc.get("pos_profile") + and self.doc.get("is_consolidated") + ): + write_off_limit = flt( + frappe.db.get_value("POS Profile", self.doc.pos_profile, "write_off_limit") + ) + if write_off_limit and abs(self.doc.outstanding_amount) <= write_off_limit: + self.doc.write_off_outstanding_amount_automatically = 1 + if ( self.doc.doctype == "Sales Invoice" and self.doc.get("is_pos") From 79ac50d0f7503cc053e7e58a382d81a705b7afbf Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 11 Aug 2022 19:31:31 +0530 Subject: [PATCH 0045/1047] fix: Unable to make payment entry against Fees using education app --- .../doctype/payment_entry/payment_entry.py | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 48edda9032..4618d0807c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -181,7 +181,11 @@ class PaymentEntry(AccountsController): frappe.throw(_("Party is mandatory")) _party_name = "title" if self.party_type == "Shareholder" else self.party_type.lower() + "_name" - self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name) + + if frappe.db.has_column(self.party_type, _party_name): + self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name) + else: + self.party_name = frappe.db.get_value(self.party_type, self.party, "name") if self.party: if not self.party_balance: @@ -295,6 +299,9 @@ class PaymentEntry(AccountsController): def validate_reference_documents(self): valid_reference_doctypes = self.get_valid_reference_doctypes() + if not valid_reference_doctypes: + return + for d in self.get("references"): if not d.allocated_amount: continue @@ -362,7 +369,7 @@ class PaymentEntry(AccountsController): if not d.allocated_amount: continue - if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"): + if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"): outstanding_amount, is_return = frappe.get_cached_value( d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"] ) @@ -1201,7 +1208,7 @@ def get_outstanding_reference_documents(args): party_account_currency = get_account_currency(args.get("party_account")) company_currency = frappe.get_cached_value("Company", args.get("company"), "default_currency") - # Get positive outstanding sales /purchase invoices/ Fees + # Get positive outstanding sales /purchase invoices condition = "" if args.get("voucher_type") and args.get("voucher_no"): condition = " and voucher_type={0} and voucher_no={1}".format( @@ -1597,10 +1604,11 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre elif reference_doctype != "Journal Entry": if not total_amount: if party_account_currency == company_currency: - total_amount = ref_doc.base_grand_total + # for handling cases that don't have multi-currency (base field) + total_amount = ref_doc.get("grand_total") or ref_doc.get("base_grand_total") exchange_rate = 1 else: - total_amount = ref_doc.grand_total + total_amount = ref_doc.get("grand_total") if not exchange_rate: # Get the exchange rate from the original ref doc # or get it based on the posting date of the ref doc. @@ -1611,7 +1619,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre if reference_doctype in ("Sales Invoice", "Purchase Invoice"): outstanding_amount = ref_doc.get("outstanding_amount") else: - outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid) + outstanding_amount = flt(total_amount) - flt(ref_doc.get("advance_paid")) else: # Get the exchange rate based on the posting date of the ref doc. @@ -1629,16 +1637,23 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre @frappe.whitelist() -def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None): +def get_payment_entry( + dt, dn, party_amount=None, bank_account=None, bank_amount=None, party_type=None, payment_type=None +): reference_doc = None doc = frappe.get_doc(dt, dn) if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0: frappe.throw(_("Can only make payment against unbilled {0}").format(dt)) - party_type = set_party_type(dt) + if not party_type: + party_type = set_party_type(dt) + party_account = set_party_account(dt, dn, doc, party_type) party_account_currency = set_party_account_currency(dt, party_account, doc) - payment_type = set_payment_type(dt, doc) + + if not payment_type: + payment_type = set_payment_type(dt, doc) + grand_total, outstanding_amount = set_grand_total_and_outstanding_amount( party_amount, dt, party_account_currency, doc ) @@ -1788,8 +1803,6 @@ def set_party_account(dt, dn, doc, party_type): party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to elif dt == "Purchase Invoice": party_account = doc.credit_to - elif dt == "Fees": - party_account = doc.receivable_account else: party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company) return party_account @@ -1805,8 +1818,7 @@ def set_party_account_currency(dt, party_account, doc): def set_payment_type(dt, doc): if ( - dt == "Sales Order" - or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0) + dt == "Sales Order" or (dt in ("Sales Invoice", "Dunning") and doc.outstanding_amount > 0) ) or (dt == "Purchase Invoice" and doc.outstanding_amount < 0): payment_type = "Receive" else: @@ -1824,18 +1836,15 @@ def set_grand_total_and_outstanding_amount(party_amount, dt, party_account_curre else: grand_total = doc.rounded_total or doc.grand_total outstanding_amount = doc.outstanding_amount - elif dt == "Fees": - grand_total = doc.grand_total - outstanding_amount = doc.outstanding_amount elif dt == "Dunning": grand_total = doc.grand_total outstanding_amount = doc.grand_total else: if party_account_currency == doc.company_currency: - grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total) + grand_total = flt(doc.get("base_rounded_total") or doc.get("base_grand_total")) else: - grand_total = flt(doc.get("rounded_total") or doc.grand_total) - outstanding_amount = grand_total - flt(doc.advance_paid) + grand_total = flt(doc.get("rounded_total") or doc.get("grand_total")) + outstanding_amount = doc.get("outstanding_amount") or (grand_total - flt(doc.advance_paid)) return grand_total, outstanding_amount From e5e88bb9f1728ce85aecda7a305dd70378c5099e Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Sat, 13 Aug 2022 11:05:48 +0530 Subject: [PATCH 0046/1047] fix: contact search in request for quotation (#31828) --- .../request_for_quotation/request_for_quotation.js | 9 ++++++--- .../request_for_quotation/request_for_quotation.py | 12 ------------ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index 4e29ee53ea..31a4837d46 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -15,9 +15,12 @@ frappe.ui.form.on("Request for Quotation",{ frm.fields_dict["suppliers"].grid.get_field("contact").get_query = function(doc, cdt, cdn) { let d = locals[cdt][cdn]; return { - query: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_supplier_contacts", - filters: {'supplier': d.supplier} - } + query: "frappe.contacts.doctype.contact.contact.contact_query", + filters: { + link_doctype: "Supplier", + link_name: d.supplier || "" + } + }; } }, 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 3ef57bb70f..ee28eb6ce2 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -286,18 +286,6 @@ def get_list_context(context=None): return list_context -@frappe.whitelist() -@frappe.validate_and_sanitize_search_inputs -def get_supplier_contacts(doctype, txt, searchfield, start, page_len, filters): - return frappe.db.sql( - """select `tabContact`.name from `tabContact`, `tabDynamic Link` - where `tabDynamic Link`.link_doctype = 'Supplier' and (`tabDynamic Link`.link_name=%(name)s - and `tabDynamic Link`.link_name like %(txt)s) and `tabContact`.name = `tabDynamic Link`.parent - limit %(page_len)s offset %(start)s""", - {"start": start, "page_len": page_len, "txt": "%%%s%%" % txt, "name": filters.get("supplier")}, - ) - - @frappe.whitelist() def make_supplier_quotation_from_rfq(source_name, target_doc=None, for_supplier=None): def postprocess(source, target_doc): From 0047e18a9b121e45798f7b7a6345feef85ee7c01 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Sat, 13 Aug 2022 11:07:22 +0530 Subject: [PATCH 0047/1047] fix: check item_code in all rows of po_items (#31741) fix: check item-code in each row of po-items --- .../manufacturing/doctype/production_plan/production_plan.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 70ccb78278..46e820542b 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -639,6 +639,9 @@ class ProductionPlan(Document): sub_assembly_items_store = [] # temporary store to process all subassembly items for row in self.po_items: + if not row.item_code: + frappe.throw(_("Row #{0}: Please select Item Code in Assembly Items").format(row.idx)) + bom_data = [] get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty) self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type) From 27891ecb77a74dea90c30133d81d92edebf59cf3 Mon Sep 17 00:00:00 2001 From: hrzzz Date: Mon, 15 Aug 2022 09:14:23 -0300 Subject: [PATCH 0048/1047] feat: two new filters for gross profit --- .../accounts/report/gross_profit/gross_profit.js | 12 ++++++++++++ .../accounts/report/gross_profit/gross_profit.py | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js index 21205c3163..ffbe9ada6c 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.js +++ b/erpnext/accounts/report/gross_profit/gross_profit.js @@ -38,6 +38,18 @@ frappe.query_reports["Gross Profit"] = { "options": "Invoice\nItem Code\nItem Group\nBrand\nWarehouse\nCustomer\nCustomer Group\nTerritory\nSales Person\nProject\nMonthly\nPayment Term", "default": "Invoice" }, + { + "fieldname":"item_group", + "label": __("Item Group"), + "fieldtype": "Link", + "options": "Item Group" + }, + { + "fieldname":"sales_person", + "label": __("Sales Person"), + "fieldtype": "Link", + "options": "Sales Person" + }, ], "tree": true, "name_field": "parent", diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 526ea9d6e2..264fcd3fcb 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -8,6 +8,7 @@ from frappe.utils import cint, flt, formatdate from erpnext.controllers.queries import get_match_cond from erpnext.stock.utils import get_incoming_rate +from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition def execute(filters=None): @@ -676,6 +677,17 @@ class GrossProfitGenerator(object): if self.filters.to_date: conditions += " and posting_date <= %(to_date)s" + if self.filters.item_group: + conditions += " and {0}".format(get_item_group_condition(self.filters.item_group)) + + if self.filters.sales_person: + conditions += """ + and exists(select 1 + from `tabSales Team` st + where st.parent = `tabSales Invoice`.name + and st.sales_person = %(sales_person)s) + """ + if self.filters.group_by == "Sales Person": sales_person_cols = ", sales.sales_person, sales.allocated_amount, sales.incentives" sales_team_table = "left join `tabSales Team` sales on sales.parent = `tabSales Invoice`.name" @@ -723,6 +735,7 @@ class GrossProfitGenerator(object): from `tabSales Invoice` inner join `tabSales Invoice Item` on `tabSales Invoice Item`.parent = `tabSales Invoice`.name + join `tabItem` item on item.name = `tabSales Invoice Item`.item_code {sales_team_table} {payment_term_table} where From 3ef551872a38c059e8ca09927c7bea44635c9474 Mon Sep 17 00:00:00 2001 From: hrzzz Date: Mon, 15 Aug 2022 09:23:56 -0300 Subject: [PATCH 0049/1047] fix: remove spaces and order import --- erpnext/accounts/report/gross_profit/gross_profit.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 264fcd3fcb..74cface4ef 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -7,8 +7,8 @@ from frappe import _, scrub from frappe.utils import cint, flt, formatdate from erpnext.controllers.queries import get_match_cond -from erpnext.stock.utils import get_incoming_rate from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition +from erpnext.stock.utils import get_incoming_rate def execute(filters=None): @@ -681,9 +681,9 @@ class GrossProfitGenerator(object): conditions += " and {0}".format(get_item_group_condition(self.filters.item_group)) if self.filters.sales_person: - conditions += """ - and exists(select 1 - from `tabSales Team` st + conditions += """ + and exists(select 1 + from `tabSales Team` st where st.parent = `tabSales Invoice`.name and st.sales_person = %(sales_person)s) """ From 3b4c0a3fc0c4fa2ab8b07ac9918352486d75a1da Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 Aug 2022 16:35:46 +0530 Subject: [PATCH 0050/1047] fix(minor): don't print tax rate if its '0' (#31838) --- erpnext/selling/page/point_of_sale/pos_item_cart.js | 3 ++- erpnext/selling/page/point_of_sale/pos_past_order_summary.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index eacf480ef8..e7dd211c0f 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -524,7 +524,8 @@ erpnext.PointOfSale.ItemCart = class { const currency = this.events.get_frm().doc.currency; const taxes_html = taxes.map(t => { if (t.tax_amount_after_discount_amount == 0.0) return; - const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; + // if tax rate is 0, don't print it. + const description = /[0-9]+/.test(t.description) ? t.description : ((t.rate != 0) ? `${t.description} @ ${t.rate}%`: t.description); return `
${description}
${format_currency(t.tax_amount_after_discount_amount, currency)}
diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index eeb8523f19..40165c3484 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -130,7 +130,8 @@ erpnext.PointOfSale.PastOrderSummary = class { if (!doc.taxes.length) return ''; let taxes_html = doc.taxes.map(t => { - const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; + // if tax rate is 0, don't print it. + const description = /[0-9]+/.test(t.description) ? t.description : ((t.rate != 0) ? `${t.description} @ ${t.rate}%`: t.description); return `
${description}
From 5fd0770372c07dbec89669e1dcdb4d98f1fbca90 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 Aug 2022 16:39:28 +0530 Subject: [PATCH 0051/1047] fix: incorrect tax amt due to different exchange rate in PR and PI --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index de3927e45b..e6ff1281d9 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1791,4 +1791,6 @@ def make_purchase_receipt(source_name, target_doc=None): target_doc, ) + doc.set_onload("ignore_price_list", True) + return doc From 538cd6fdcf2cc627515b7effd0e98f3538a7a11a Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Wed, 17 Aug 2022 13:01:56 +0530 Subject: [PATCH 0052/1047] fix: incorrect produced-qty in production-plan-item (#31706) --- .../production_plan/production_plan.py | 1 - .../production_plan/test_production_plan.py | 60 ++++++++++++++++--- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 46e820542b..2cdf8d3ea9 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -482,7 +482,6 @@ class ProductionPlan(Document): "bom_no", "stock_uom", "bom_level", - "production_plan_item", "schedule_date", ]: if row.get(field): diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 040e791e00..e2415ad848 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -11,8 +11,9 @@ from erpnext.manufacturing.doctype.production_plan.production_plan import ( get_warehouse_list, ) from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError +from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry as make_se_from_wo from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order -from erpnext.stock.doctype.item.test_item import create_item +from erpnext.stock.doctype.item.test_item import create_item, make_item from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( create_stock_reconciliation, @@ -583,9 +584,6 @@ class TestProductionPlan(FrappeTestCase): Test Prod Plan impact via: SO -> Prod Plan -> WO -> SE -> SE (cancel) """ from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record - from erpnext.manufacturing.doctype.work_order.work_order import ( - make_stock_entry as make_se_from_wo, - ) make_stock_entry( item_code="Raw Material Item 1", target="Work In Progress - _TC", qty=2, basic_rate=100 @@ -629,9 +627,6 @@ class TestProductionPlan(FrappeTestCase): def test_production_plan_pending_qty_independent_items(self): "Test Prod Plan impact if items are added independently (no from SO or MR)." from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record - from erpnext.manufacturing.doctype.work_order.work_order import ( - make_stock_entry as make_se_from_wo, - ) make_stock_entry( item_code="Raw Material Item 1", target="Work In Progress - _TC", qty=2, basic_rate=100 @@ -728,6 +723,57 @@ class TestProductionPlan(FrappeTestCase): for po_item, subassy_item in zip(pp.po_items, pp.sub_assembly_items): self.assertEqual(po_item.name, subassy_item.production_plan_item) + def test_produced_qty_for_multi_level_bom_item(self): + # Create Items and BOMs + rm_item = make_item(properties={"is_stock_item": 1}).name + sub_assembly_item = make_item(properties={"is_stock_item": 1}).name + fg_item = make_item(properties={"is_stock_item": 1}).name + + make_stock_entry( + item_code=rm_item, + qty=60, + to_warehouse="Work In Progress - _TC", + rate=99, + purpose="Material Receipt", + ) + + make_bom(item=sub_assembly_item, raw_materials=[rm_item], rm_qty=3) + make_bom(item=fg_item, raw_materials=[sub_assembly_item], rm_qty=4) + + # Step - 1: Create Production Plan + pln = create_production_plan(item_code=fg_item, planned_qty=5, skip_getting_mr_items=1) + pln.get_sub_assembly_items() + + # Step - 2: Create Work Orders + pln.make_work_order() + work_orders = frappe.get_all("Work Order", filters={"production_plan": pln.name}, pluck="name") + sa_wo = fg_wo = None + for work_order in work_orders: + wo_doc = frappe.get_doc("Work Order", work_order) + if wo_doc.production_plan_item: + wo_doc.update( + {"wip_warehouse": "Work In Progress - _TC", "fg_warehouse": "Finished Goods - _TC"} + ) + fg_wo = wo_doc.name + else: + wo_doc.update( + {"wip_warehouse": "Work In Progress - _TC", "fg_warehouse": "Work In Progress - _TC"} + ) + sa_wo = wo_doc.name + wo_doc.submit() + + # Step - 3: Complete Work Orders + se = frappe.get_doc(make_se_from_wo(sa_wo, "Manufacture")) + se.submit() + + se = frappe.get_doc(make_se_from_wo(fg_wo, "Manufacture")) + se.submit() + + # Step - 4: Check Production Plan Item Produced Qty + pln.load_from_db() + self.assertEqual(pln.status, "Completed") + self.assertEqual(pln.po_items[0].produced_qty, 5) + def create_production_plan(**args): """ From 313625c3497d7db2d86a2835c458ac1028c27fe8 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Wed, 17 Aug 2022 13:51:53 +0530 Subject: [PATCH 0053/1047] fix: incorrect rate in BOM exploded items (#31513) --- erpnext/manufacturing/doctype/bom/bom.py | 2 +- erpnext/manufacturing/doctype/bom/test_bom.py | 28 +++++++++++++++++++ .../doctype/bom_item/bom_item.json | 3 +- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index b29f6710e1..70637d3ef2 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -189,8 +189,8 @@ class BOM(WebsiteGenerator): self.validate_transfer_against() self.set_routing_operations() self.validate_operations() - self.update_exploded_items(save=False) self.calculate_cost() + self.update_exploded_items(save=False) self.update_stock_qty() self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate=False, save=False) self.validate_scrap_items() diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index a190cc7430..27f3cc905b 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -611,6 +611,34 @@ class TestBOM(FrappeTestCase): bom.reload() self.assertEqual(frappe.get_value("Item", fg_item.item_code, "default_bom"), bom.name) + def test_exploded_items_rate(self): + rm_item = make_item( + properties={"is_stock_item": 1, "valuation_rate": 99, "last_purchase_rate": 89} + ).name + fg_item = make_item(properties={"is_stock_item": 1}).name + + from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom + + bom = make_bom(item=fg_item, raw_materials=[rm_item], do_not_save=True) + + bom.rm_cost_as_per = "Last Purchase Rate" + bom.save() + self.assertEqual(bom.items[0].base_rate, 89) + self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate) + + bom.rm_cost_as_per = "Price List" + bom.save() + self.assertEqual(bom.items[0].base_rate, 0.0) + self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate) + + bom.rm_cost_as_per = "Valuation Rate" + bom.save() + self.assertEqual(bom.items[0].base_rate, 99) + self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate) + + bom.submit() + self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate) + def get_default_bom(item_code="_Test FG Item 2"): return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1}) diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json index 0a8ae7b4a7..c5266119dc 100644 --- a/erpnext/manufacturing/doctype/bom_item/bom_item.json +++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json @@ -184,6 +184,7 @@ "in_list_view": 1, "label": "Rate", "options": "currency", + "read_only": 1, "reqd": 1 }, { @@ -288,7 +289,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-05-19 02:32:43.785470", + "modified": "2022-07-28 10:20:51.559010", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Item", From 795c94384a5dd8919b0dc989e19d8b9ee81071d4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 17 Aug 2022 13:48:56 +0530 Subject: [PATCH 0054/1047] fix: not able to issue expired batches --- erpnext/controllers/stock_controller.py | 14 +++++++++- erpnext/stock/doctype/batch/test_batch.py | 8 +++++- .../doctype/stock_entry/test_stock_entry.py | 27 ++++++++++++++++++- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index e27718a9b4..f9fc5f60bb 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -36,6 +36,10 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass +class BatchExpiredError(frappe.ValidationError): + pass + + class StockController(AccountsController): def validate(self): super(StockController, self).validate() @@ -77,6 +81,10 @@ class StockController(AccountsController): def validate_serialized_batch(self): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + is_material_issue = False + if self.doctype == "Stock Entry" and self.purpose == "Material Issue": + is_material_issue = True + for d in self.get("items"): if hasattr(d, "serial_no") and hasattr(d, "batch_no") and d.serial_no and d.batch_no: serial_nos = frappe.get_all( @@ -93,6 +101,9 @@ class StockController(AccountsController): ) ) + if is_material_issue: + continue + if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2: expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date") @@ -100,7 +111,8 @@ class StockController(AccountsController): frappe.throw( _("Row #{0}: The batch {1} has already expired.").format( d.idx, get_link_to_form("Batch", d.get("batch_no")) - ) + ), + BatchExpiredError, ) def clean_serial_nos(self): diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py index 3e470d4ce4..271e2e0298 100644 --- a/erpnext/stock/doctype/batch/test_batch.py +++ b/erpnext/stock/doctype/batch/test_batch.py @@ -473,7 +473,13 @@ def make_new_batch(**args): "doctype": "Batch", "batch_id": args.batch_id, "item": args.item_code, + "expiry_date": args.expiry_date, } - ).insert() + ) + + if args.expiry_date: + batch.expiry_date = args.expiry_date + + batch.insert() return batch diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index a2f9978670..b574b718fe 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -5,7 +5,7 @@ import frappe from frappe.permissions import add_user_permission, remove_user_permission from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, flt, nowdate, nowtime +from frappe.utils import add_days, flt, nowdate, nowtime, today from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.stock.doctype.item.test_item import ( @@ -1589,6 +1589,31 @@ class TestStockEntry(FrappeTestCase): self.assertEqual(obj.items[index].basic_rate, 200) self.assertEqual(obj.items[index].basic_amount, 2000) + def test_batch_expiry(self): + from erpnext.controllers.stock_controller import BatchExpiredError + from erpnext.stock.doctype.batch.test_batch import make_new_batch + + item_code = "Test Batch Expiry Test Item - 001" + item_doc = create_item(item_code=item_code, is_stock_item=1, valuation_rate=10) + + item_doc.has_batch_no = 1 + item_doc.save() + + batch = make_new_batch( + batch_id=frappe.generate_hash("", 5), item_code=item_doc.name, expiry_date=add_days(today(), -1) + ) + + se = make_stock_entry( + item_code=item_code, + purpose="Material Receipt", + qty=4, + to_warehouse="_Test Warehouse - _TC", + batch_no=batch.name, + do_not_save=True, + ) + + self.assertRaises(BatchExpiredError, se.save) + def make_serialized_item(**args): args = frappe._dict(args) From 0b39a0123e2e696007bbe196dad490d4534be610 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 17 Aug 2022 11:56:13 +0530 Subject: [PATCH 0055/1047] fix: delete custom fields on deletion of inventory dimension --- erpnext/controllers/stock_controller.py | 17 +++- .../inventory_dimension.js | 31 +++++++- .../inventory_dimension.json | 15 +++- .../inventory_dimension.py | 78 ++++++++++++++----- .../test_inventory_dimension.py | 27 +++++++ 5 files changed, 143 insertions(+), 25 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index e27718a9b4..440271cac7 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -374,9 +374,24 @@ class StockController(AccountsController): def update_inventory_dimensions(self, row, sl_dict) -> None: dimensions = get_evaluated_inventory_dimension(row, sl_dict, parent_doc=self) for dimension in dimensions: - if dimension and row.get(dimension.source_fieldname): + if not dimension: + continue + + if row.get(dimension.source_fieldname): sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname) + if not sl_dict.get(dimension.target_fieldname) and dimension.fetch_from_parent: + sl_dict[dimension.target_fieldname] = self.get(dimension.fetch_from_parent) + + # Get value based on doctype name + if not sl_dict.get(dimension.target_fieldname): + fieldname = frappe.get_cached_value( + "DocField", {"parent": self.doctype, "options": dimension.fetch_from_parent}, "fieldname" + ) + + if fieldname and self.get(fieldname): + sl_dict[dimension.target_fieldname] = self.get(fieldname) + def make_sl_entries(self, sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False): from erpnext.stock.stock_ledger import make_sl_entries diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js index 91a21f4e72..07cb73b1d5 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js @@ -35,14 +35,39 @@ frappe.ui.form.on('Inventory Dimension', { refresh(frm) { if (frm.doc.__onload && frm.doc.__onload.has_stock_ledger && frm.doc.__onload.has_stock_ledger.length) { - let msg = __('Stock transactions exists against this dimension, user can not update document.'); - frm.dashboard.add_comment(msg, 'blue', true); + let allow_to_edit_fields = ['disabled', 'fetch_from_parent', + 'type_of_transaction', 'condition']; frm.fields.forEach((field) => { - if (field.df.fieldname !== 'disabled') { + if (!in_list(allow_to_edit_fields, field.df.fieldname)) { frm.set_df_property(field.df.fieldname, "read_only", "1"); } }); } + + if (!frm.is_new()) { + frm.add_custom_button(__('Delete Dimension'), () => { + frm.trigger('delete_dimension'); + }); + } + }, + + delete_dimension(frm) { + let msg = (` + Custom fields related to this dimension will be deleted on deletion of dimension. +
Do you want to delete {0} dimension? + `); + + frappe.confirm(__(msg, [frm.doc.name.bold()]), () => { + frappe.call({ + method: 'erpnext.stock.doctype.inventory_dimension.inventory_dimension.delete_dimension', + args: { + dimension: frm.doc.name + }, + callback: function() { + frappe.set_route('List', 'Inventory Dimension'); + } + }); + }); } }); diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json index 8b334d13d7..03e7fda841 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json @@ -1,6 +1,5 @@ { "actions": [], - "allow_rename": 1, "autoname": "field:dimension_name", "creation": "2022-06-17 13:04:16.554051", "doctype": "DocType", @@ -22,6 +21,7 @@ "document_type", "istable", "type_of_transaction", + "fetch_from_parent", "column_break_16", "condition", "applicable_condition_example_section", @@ -101,12 +101,14 @@ "fieldname": "target_fieldname", "fieldtype": "Data", "label": "Target Fieldname (Stock Ledger Entry)", + "no_copy": 1, "read_only": 1 }, { "fieldname": "source_fieldname", "fieldtype": "Data", "label": "Source Fieldname", + "no_copy": 1, "read_only": 1 }, { @@ -123,7 +125,7 @@ "fieldname": "type_of_transaction", "fieldtype": "Select", "label": "Type of Transaction", - "options": "\nInward\nOutward" + "options": "\nInward\nOutward\nBoth" }, { "fieldname": "html_19", @@ -140,11 +142,18 @@ { "fieldname": "column_break_4", "fieldtype": "Column Break" + }, + { + "depends_on": "istable", + "description": "Set fieldname or DocType name like Supplier, Customer etc.", + "fieldname": "fetch_from_parent", + "fieldtype": "Data", + "label": "Fetch Value From Parent Form" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2022-07-19 21:06:11.824976", + "modified": "2022-08-17 11:43:24.722441", "modified_by": "Administrator", "module": "Stock", "name": "Inventory Dimension", diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py index 5a9541f060..4ff8f33b40 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py @@ -43,13 +43,37 @@ class InventoryDimension(Document): return old_doc = self._doc_before_save + allow_to_edit_fields = [ + "disabled", + "fetch_from_parent", + "type_of_transaction", + "condition", + ] + for field in frappe.get_meta("Inventory Dimension").fields: - if field.fieldname != "disabled" and old_doc.get(field.fieldname) != self.get(field.fieldname): + if field.fieldname not in allow_to_edit_fields and old_doc.get(field.fieldname) != self.get( + field.fieldname + ): msg = f"""The user can not change value of the field {bold(field.label)} because stock transactions exists against the dimension {bold(self.name)}.""" frappe.throw(_(msg), DoNotChangeError) + def on_trash(self): + self.delete_custom_fields() + + def delete_custom_fields(self): + filters = {"fieldname": self.source_fieldname} + + if self.document_type: + filters["dt"] = self.document_type + + for field in frappe.get_all("Custom Field", filters=filters): + frappe.delete_doc("Custom Field", field.name) + + msg = f"Deleted custom fields related to the dimension {self.name}" + frappe.msgprint(_(msg)) + def reset_value(self): if self.apply_to_all_doctypes: self.istable = 0 @@ -76,30 +100,35 @@ class InventoryDimension(Document): self.add_custom_fields() def add_custom_fields(self): - dimension_field = dict( - fieldname=self.source_fieldname, - fieldtype="Link", - insert_after="warehouse", - options=self.reference_document, - label=self.dimension_name, - ) + dimension_fields = [ + dict( + fieldname="inventory_dimension", + fieldtype="Section Break", + insert_after="warehouse", + label="Inventory Dimension", + collapsible=1, + ), + dict( + fieldname=self.source_fieldname, + fieldtype="Link", + insert_after="inventory_dimension", + options=self.reference_document, + label=self.dimension_name, + ), + ] custom_fields = {} if self.apply_to_all_doctypes: for doctype in get_inventory_documents(): - if not frappe.db.get_value( - "Custom Field", {"dt": doctype[0], "fieldname": self.source_fieldname} - ): - custom_fields.setdefault(doctype[0], dimension_field) - elif not frappe.db.get_value( - "Custom Field", {"dt": self.document_type, "fieldname": self.source_fieldname} - ): - custom_fields.setdefault(self.document_type, dimension_field) + custom_fields.setdefault(doctype[0], dimension_fields) + else: + custom_fields.setdefault(self.document_type, dimension_fields) if not frappe.db.get_value( "Custom Field", {"dt": "Stock Ledger Entry", "fieldname": self.target_fieldname} ): + dimension_field = dimension_fields[1] dimension_field["fieldname"] = self.target_fieldname custom_fields["Stock Ledger Entry"] = dimension_field @@ -143,7 +172,7 @@ def get_evaluated_inventory_dimension(doc, sl_dict, parent_doc=None): elif ( row.type_of_transaction == "Outward" if doc.docstatus == 1 - else row.type_of_transaction != "Inward" + else row.type_of_transaction != "Outward" ) and sl_dict.actual_qty > 0: continue @@ -166,7 +195,14 @@ def get_document_wise_inventory_dimensions(doctype) -> dict: if not frappe.local.document_wise_inventory_dimensions.get(doctype): dimensions = frappe.get_all( "Inventory Dimension", - fields=["name", "source_fieldname", "condition", "target_fieldname", "type_of_transaction"], + fields=[ + "name", + "source_fieldname", + "condition", + "target_fieldname", + "type_of_transaction", + "fetch_from_parent", + ], filters={"disabled": 0}, or_filters={"document_type": doctype, "apply_to_all_doctypes": 1}, ) @@ -194,3 +230,9 @@ def get_inventory_dimensions(): frappe.local.inventory_dimensions = dimensions return frappe.local.inventory_dimensions + + +@frappe.whitelist() +def delete_dimension(dimension): + doc = frappe.get_doc("Inventory Dimension", dimension) + doc.delete() diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py index 998a0e9d6a..cc90b74ee8 100644 --- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py @@ -8,6 +8,7 @@ from erpnext.stock.doctype.inventory_dimension.inventory_dimension import ( CanNotBeChildDoc, CanNotBeDefaultDimension, DoNotChangeError, + delete_dimension, ) from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse @@ -42,6 +43,32 @@ class TestInventoryDimension(FrappeTestCase): self.assertRaises(CanNotBeDefaultDimension, inv_dim1.insert) + def test_delete_inventory_dimension(self): + inv_dim1 = create_inventory_dimension( + reference_document="Shelf", + type_of_transaction="Outward", + dimension_name="From Shelf", + apply_to_all_doctypes=0, + document_type="Stock Entry Detail", + condition="parent.purpose == 'Material Issue'", + ) + + inv_dim1.save() + + custom_field = frappe.db.get_value( + "Custom Field", {"fieldname": "from_shelf", "dt": "Stock Entry Detail"}, "name" + ) + + self.assertTrue(custom_field) + + delete_dimension(inv_dim1.name) + + custom_field = frappe.db.get_value( + "Custom Field", {"fieldname": "from_shelf", "dt": "Stock Entry Detail"}, "name" + ) + + self.assertFalse(custom_field) + def test_inventory_dimension(self): warehouse = "Shelf Warehouse - _TC" item_code = "_Test Item" From 2d04e7141271eaded41d53bbc9a22b37fbd5816f Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Wed, 17 Aug 2022 15:57:41 +0530 Subject: [PATCH 0056/1047] fix: Make expense account editable in Subcontracting Receipt Item (#31848) --- erpnext/controllers/stock_controller.py | 8 +++++++- .../subcontracting_receipt.js | 7 +++++++ .../subcontracting_receipt_item.json | 14 +++++++++----- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 49f85cdba5..36bed36484 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -322,7 +322,13 @@ class StockController(AccountsController): ) if ( self.doctype - not in ("Purchase Receipt", "Purchase Invoice", "Stock Reconciliation", "Stock Entry") + not in ( + "Purchase Receipt", + "Purchase Invoice", + "Stock Reconciliation", + "Stock Entry", + "Subcontracting Receipt", + ) and not is_expense_account ): frappe.throw( diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index b2506cd143..35fec8bc33 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -48,6 +48,13 @@ frappe.ui.form.on('Subcontracting Receipt', { is_group: 0 } })); + + frm.set_query("expense_account", "items", function () { + return { + query: "erpnext.controllers.queries.get_expense_account", + filters: { 'company': frm.doc.company } + }; + }); }, refresh: (frm) => { diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json index e2785ce0cd..bf15f0f688 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -49,11 +49,12 @@ "col_break5", "batch_no", "rejected_serial_no", - "expense_account", "manufacture_details", "manufacturer", "column_break_16", "manufacturer_part_no", + "accounting_details_section", + "expense_account", "accounting_dimensions_section", "project", "dimension_col_break", @@ -363,10 +364,8 @@ { "fieldname": "expense_account", "fieldtype": "Link", - "hidden": 1, "label": "Expense Account", - "options": "Account", - "read_only": 1 + "options": "Account" }, { "collapsible": 1, @@ -456,12 +455,17 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "accounting_details_section", + "fieldtype": "Section Break", + "label": "Accounting Details" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2022-04-21 12:07:55.899701", + "modified": "2022-08-15 11:34:00.970042", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item", From 967dd398e7ff7207aaa4c7f5808bcac6084d44cb Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 Aug 2022 09:27:42 +0530 Subject: [PATCH 0057/1047] fix: incorrect buying amount in Gross Profit rpt --- erpnext/accounts/report/gross_profit/gross_profit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 526ea9d6e2..54af225915 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -616,7 +616,7 @@ class GrossProfitGenerator(object): previous_stock_value = len(my_sle) > i + 1 and flt(my_sle[i + 1].stock_value) or 0.0 if previous_stock_value: - return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty)) + return abs(previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty)) else: return flt(row.qty) * self.get_average_buying_rate(row, item_code) else: From f1a612245c05aa56f7654694495d4ec5b9d3d8b9 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Wed, 17 Aug 2022 16:44:12 +0530 Subject: [PATCH 0058/1047] fix: Transit filter for Default Target Warehouse in SE (#31839) --- .../stock/doctype/stock_entry/stock_entry.js | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 1c514a90ee..e3a8438d95 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -583,18 +583,23 @@ frappe.ui.form.on('Stock Entry', { }, add_to_transit: function(frm) { - if(frm.doc.add_to_transit && frm.doc.purpose=='Material Transfer') { - frm.set_value('to_warehouse', ''); + if(frm.doc.purpose=='Material Transfer') { + var filters = { + 'is_group': 0, + 'company': frm.doc.company + } + + if(frm.doc.add_to_transit){ + filters['warehouse_type'] = 'Transit'; + frm.set_value('to_warehouse', ''); + frm.trigger('set_transit_warehouse'); + } + frm.fields_dict.to_warehouse.get_query = function() { return { - filters:{ - 'warehouse_type' : 'Transit', - 'is_group': 0, - 'company': frm.doc.company - } + filters:filters }; }; - frm.trigger('set_transit_warehouse'); } }, From d38778e40086d31593d3d34f926d9aa4324c963c Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Wed, 17 Aug 2022 18:21:43 +0530 Subject: [PATCH 0059/1047] fix(projects): Add missing comma Added with https://github.com/frappe/erpnext/pull/31360 --- erpnext/projects/doctype/project/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index e78e4b6577..a2be936768 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -379,7 +379,7 @@ def get_users_for_project(doctype, txt, searchfield, start, page_len, filters): {fcond} {mcond} order by (case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end), - (case when locate(%(_txt)s, full_name) > 0 then locate(%(_txt)s, full_name) else 99999 end) + (case when locate(%(_txt)s, full_name) > 0 then locate(%(_txt)s, full_name) else 99999 end), idx desc, name, full_name limit %(page_len)s offset %(start)s""".format( From 8704ca783d3f3280113d11444baa8acfeb78c2a3 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 18 Aug 2022 10:58:33 +0530 Subject: [PATCH 0060/1047] fix: Add dimension section in subcontracting doctypes (#31849) --- erpnext/hooks.py | 4 ++ erpnext/patches.txt | 3 +- ...g_dimensions_in_subcontracting_doctypes.py | 47 +++++++++++++++++++ .../subcontracting_order.json | 28 ++++++++++- .../subcontracting_order_item.json | 28 ++++++++++- .../subcontracting_receipt.json | 28 ++++++++++- .../subcontracting_receipt_item.json | 6 +-- 7 files changed, 137 insertions(+), 7 deletions(-) create mode 100644 erpnext/patches/v14_0/create_accounting_dimensions_in_subcontracting_doctypes.py diff --git a/erpnext/hooks.py b/erpnext/hooks.py index c4f0c59c38..a08feb4447 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -520,6 +520,10 @@ accounting_dimension_doctypes = [ "Purchase Order", "Purchase Receipt", "Sales Order", + "Subcontracting Order", + "Subcontracting Order Item", + "Subcontracting Receipt", + "Subcontracting Receipt Item", ] # get matching queries for Bank Reconciliation diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e5beacde2b..d92353aca4 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -310,4 +310,5 @@ erpnext.patches.v14_0.crm_ux_cleanup erpnext.patches.v14_0.remove_india_localisation # 14-07-2022 erpnext.patches.v13_0.fix_number_and_frequency_for_monthly_depreciation erpnext.patches.v14_0.remove_hr_and_payroll_modules # 20-07-2022 -erpnext.patches.v14_0.fix_crm_no_of_employees \ No newline at end of file +erpnext.patches.v14_0.fix_crm_no_of_employees +erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes \ No newline at end of file diff --git a/erpnext/patches/v14_0/create_accounting_dimensions_in_subcontracting_doctypes.py b/erpnext/patches/v14_0/create_accounting_dimensions_in_subcontracting_doctypes.py new file mode 100644 index 0000000000..b349c07f6d --- /dev/null +++ b/erpnext/patches/v14_0/create_accounting_dimensions_in_subcontracting_doctypes.py @@ -0,0 +1,47 @@ +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_field + + +def execute(): + accounting_dimensions = frappe.db.get_all( + "Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"] + ) + + if not accounting_dimensions: + return + + count = 1 + for d in accounting_dimensions: + + if count % 2 == 0: + insert_after_field = "dimension_col_break" + else: + insert_after_field = "accounting_dimensions_section" + + for doctype in [ + "Subcontracting Order", + "Subcontracting Order Item", + "Subcontracting Receipt", + "Subcontracting Receipt Item", + ]: + + field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname}) + + if field: + continue + + df = { + "fieldname": d.fieldname, + "label": d.label, + "fieldtype": "Link", + "options": d.document_type, + "insert_after": insert_after_field, + } + + try: + create_custom_field(doctype, df, ignore_validate=True) + frappe.clear_cache(doctype=doctype) + except Exception: + pass + + count += 1 diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json index c6e76c76d7..f98f559d5c 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json @@ -19,6 +19,10 @@ "transaction_date", "schedule_date", "amended_from", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break", + "project", "address_and_contact_section", "supplier_address", "address_display", @@ -422,12 +426,34 @@ "fieldtype": "Select", "label": "Distribute Additional Costs Based On ", "options": "Qty\nAmount" + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" } ], "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2022-04-11 21:02:44.097841", + "modified": "2022-08-15 14:08:49.204218", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Order", diff --git a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json index 291f47a634..3675a4ea08 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json @@ -40,6 +40,10 @@ "manufacture_section", "manufacturer", "manufacturer_part_no", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break", + "project", "section_break_34", "page_break" ], @@ -304,13 +308,35 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-04-11 21:28:06.585338", + "modified": "2022-08-15 14:25:45.177703", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Order Item", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index e9638144a7..9430486560 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -17,6 +17,10 @@ "posting_time", "is_return", "return_against", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break", + "project", "section_addresses", "supplier_address", "contact_person", @@ -569,11 +573,33 @@ { "fieldname": "section_break_47", "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions " + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" } ], "is_submittable": 1, "links": [], - "modified": "2022-04-18 13:15:12.011682", + "modified": "2022-08-15 14:30:29.447307", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json index bf15f0f688..437fc41f5e 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -56,9 +56,9 @@ "accounting_details_section", "expense_account", "accounting_dimensions_section", - "project", - "dimension_col_break", "cost_center", + "dimension_col_break", + "project", "section_break_80", "page_break" ], @@ -465,7 +465,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-08-15 11:34:00.970042", + "modified": "2022-08-15 14:51:10.613347", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item", From 1a6508972e3555aabd06d93a46af9cc513e32819 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 18 Aug 2022 10:59:39 +0530 Subject: [PATCH 0061/1047] fix: Make expense account editable in Purchase Receipt Item (#31730) Co-authored-by: Sagar Sharma --- .../purchase_receipt_item/purchase_receipt_item.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index c97dbee911..39833b5e91 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -792,10 +792,8 @@ { "fieldname": "expense_account", "fieldtype": "Link", - "hidden": 1, "label": "Expense Account", - "options": "Account", - "read_only": 1 + "options": "Account" }, { "fieldname": "accounting_dimensions_section", @@ -1001,7 +999,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-06-17 05:32:16.483178", + "modified": "2022-07-28 19:27:54.880781", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From d7ed4093d8f494affb9929f7ad86cc954e48a8e1 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 18 Aug 2022 16:45:11 +0530 Subject: [PATCH 0062/1047] fix: additional-cost in items table --- .../subcontracting_order.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index 71cdc94a3a..1fd0746461 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -83,23 +83,23 @@ class SubcontractingOrder(SubcontractingController): self.set_missing_values_in_items() def set_missing_values_in_additional_costs(self): - if self.get("additional_costs"): - self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs")) + self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs")) - if self.total_additional_costs: - if self.distribute_additional_costs_based_on == "Amount": - total_amt = sum(flt(item.amount) for item in self.get("items")) - for item in self.items: - item.additional_cost_per_qty = ( - (item.amount * self.total_additional_costs) / total_amt - ) / item.qty - else: - total_qty = sum(flt(item.qty) for item in self.get("items")) - additional_cost_per_qty = self.total_additional_costs / total_qty - for item in self.items: - item.additional_cost_per_qty = additional_cost_per_qty + if self.total_additional_costs: + if self.distribute_additional_costs_based_on == "Amount": + total_amt = sum(flt(item.amount) for item in self.get("items")) + for item in self.items: + item.additional_cost_per_qty = ( + (item.amount * self.total_additional_costs) / total_amt + ) / item.qty + else: + total_qty = sum(flt(item.qty) for item in self.get("items")) + additional_cost_per_qty = self.total_additional_costs / total_qty + for item in self.items: + item.additional_cost_per_qty = additional_cost_per_qty else: - self.total_additional_costs = 0 + for item in self.items: + item.additional_cost_per_qty = 0 def set_missing_values_in_service_items(self): for idx, item in enumerate(self.get("service_items")): From eabd3135f0f9d0ca788729c9391fd72ee8a6f871 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 18 Aug 2022 17:16:29 +0530 Subject: [PATCH 0063/1047] fix: base_amount and exchange_rate in additional-cost table --- erpnext/stock/landed_taxes_and_charges_common.js | 2 +- .../subcontracting_order/subcontracting_order.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/landed_taxes_and_charges_common.js b/erpnext/stock/landed_taxes_and_charges_common.js index ff8a69fb03..78bfd5dd1a 100644 --- a/erpnext/stock/landed_taxes_and_charges_common.js +++ b/erpnext/stock/landed_taxes_and_charges_common.js @@ -1,4 +1,4 @@ -let document_list = ['Landed Cost Voucher', 'Stock Entry']; +let document_list = ['Landed Cost Voucher', 'Stock Entry', 'Subcontracting Order']; document_list.forEach((doctype) => { frappe.ui.form.on(doctype, { diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js index dbd337afd4..c20f8ab665 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js @@ -3,6 +3,8 @@ frappe.provide('erpnext.buying'); +{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %}; + frappe.ui.form.on('Subcontracting Order', { setup: (frm) => { frm.get_field("items").grid.cannot_add_rows = true; @@ -136,6 +138,16 @@ frappe.ui.form.on('Subcontracting Order', { } }); +frappe.ui.form.on('Landed Cost Taxes and Charges', { + amount: function (frm, cdt, cdn) { + frm.events.set_base_amount(frm, cdt, cdn); + }, + + expense_account: function (frm, cdt, cdn) { + frm.events.set_account_currency(frm, cdt, cdn); + } +}); + erpnext.buying.SubcontractingOrderController = class SubcontractingOrderController { setup() { this.frm.custom_make_buttons = { From ea82fe5bc20e4a06c40b9840c2ec536208c3fc3b Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 18 Aug 2022 17:20:22 +0530 Subject: [PATCH 0064/1047] chore: move "set_missing_values_in_additional_costs" from SCO to SC" --- .../controllers/subcontracting_controller.py | 19 +++++++++++++++++++ .../subcontracting_order.py | 19 ------------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 2a2f8f562e..a944cb8fcf 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -720,6 +720,25 @@ class SubcontractingController(StockController): sco_doc = frappe.get_doc("Subcontracting Order", sco) sco_doc.update_status() + def set_missing_values_in_additional_costs(self): + self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs")) + + if self.total_additional_costs: + if self.distribute_additional_costs_based_on == "Amount": + total_amt = sum(flt(item.amount) for item in self.get("items")) + for item in self.items: + item.additional_cost_per_qty = ( + (item.amount * self.total_additional_costs) / total_amt + ) / item.qty + else: + total_qty = sum(flt(item.qty) for item in self.get("items")) + additional_cost_per_qty = self.total_additional_costs / total_qty + for item in self.items: + item.additional_cost_per_qty = additional_cost_per_qty + else: + for item in self.items: + item.additional_cost_per_qty = 0 + @frappe.whitelist() def get_current_stock(self): if self.doctype in ["Purchase Receipt", "Subcontracting Receipt"]: diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index 1fd0746461..0495fb4174 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -82,25 +82,6 @@ class SubcontractingOrder(SubcontractingController): self.set_missing_values_in_supplied_items() self.set_missing_values_in_items() - def set_missing_values_in_additional_costs(self): - self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs")) - - if self.total_additional_costs: - if self.distribute_additional_costs_based_on == "Amount": - total_amt = sum(flt(item.amount) for item in self.get("items")) - for item in self.items: - item.additional_cost_per_qty = ( - (item.amount * self.total_additional_costs) / total_amt - ) / item.qty - else: - total_qty = sum(flt(item.qty) for item in self.get("items")) - additional_cost_per_qty = self.total_additional_costs / total_qty - for item in self.items: - item.additional_cost_per_qty = additional_cost_per_qty - else: - for item in self.items: - item.additional_cost_per_qty = 0 - def set_missing_values_in_service_items(self): for idx, item in enumerate(self.get("service_items")): self.items[idx].service_cost_per_qty = item.amount / self.items[idx].qty From 7e88eb549f7059e494455c642e966bb72d0dc5e7 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 18 Aug 2022 17:39:00 +0530 Subject: [PATCH 0065/1047] chore: remove unwanted field "provisional_expense_account" from SCR (#31847) --- .../subcontracting_receipt.json | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index 9430486560..cb5b8c06dd 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -51,8 +51,6 @@ "in_words", "bill_no", "bill_date", - "accounting_details_section", - "provisional_expense_account", "more_info", "status", "column_break_39", @@ -524,19 +522,6 @@ "options": "Company", "read_only": 1 }, - { - "collapsible": 1, - "fieldname": "accounting_details_section", - "fieldtype": "Section Break", - "label": "Accounting Details" - }, - { - "fieldname": "provisional_expense_account", - "fieldtype": "Link", - "hidden": 1, - "label": "Provisional Expense Account", - "options": "Account" - }, { "default": "0", "fieldname": "is_return", @@ -599,7 +584,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2022-08-15 14:30:29.447307", + "modified": "2022-08-16 09:56:41.199435", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", From 2fc683368403cf6341bf09fcfbd367f9777efa94 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 18 Aug 2022 19:50:00 +0530 Subject: [PATCH 0066/1047] fix: recalculate rate of items based on "Recalculate Rate" checkbox --- .../subcontracting_receipt/subcontracting_receipt.py | 2 +- .../subcontracting_receipt_item.json | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 0c4ec6fb76..51007c5635 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -125,7 +125,7 @@ class SubcontractingReceipt(SubcontractingController): item.rm_cost_per_qty = item.rm_supp_cost / item.qty rm_supp_cost.pop(item.name) - if self.is_new() and item.rm_supp_cost > 0: + if item.recalculate_rate: item.rate = ( item.rm_cost_per_qty + (item.service_cost_per_qty or 0) + item.additional_cost_per_qty ) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json index 437fc41f5e..dcde635992 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -29,6 +29,7 @@ "rate_and_amount", "rate", "amount", + "recalculate_rate", "column_break_19", "rm_cost_per_qty", "service_cost_per_qty", @@ -460,12 +461,18 @@ "fieldname": "accounting_details_section", "fieldtype": "Section Break", "label": "Accounting Details" + }, + { + "default": "1", + "fieldname": "recalculate_rate", + "fieldtype": "Check", + "label": "Recalculate Rate" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2022-08-15 14:51:10.613347", + "modified": "2022-08-18 19:42:24.313449", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item", From 256b4245d51d28cccbca2d0c0037961b0a16112f Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 18 Aug 2022 20:26:34 +0530 Subject: [PATCH 0067/1047] chore: add additional-cost table in SCR --- .../stock/landed_taxes_and_charges_common.js | 2 +- .../subcontracting_receipt.js | 12 +++++++ .../subcontracting_receipt.json | 34 ++++++++++++++++++- .../subcontracting_receipt.py | 1 + 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/landed_taxes_and_charges_common.js b/erpnext/stock/landed_taxes_and_charges_common.js index 78bfd5dd1a..1d76a3d95a 100644 --- a/erpnext/stock/landed_taxes_and_charges_common.js +++ b/erpnext/stock/landed_taxes_and_charges_common.js @@ -1,4 +1,4 @@ -let document_list = ['Landed Cost Voucher', 'Stock Entry', 'Subcontracting Order']; +let document_list = ['Landed Cost Voucher', 'Stock Entry', 'Subcontracting Order', 'Subcontracting Receipt']; document_list.forEach((doctype) => { frappe.ui.form.on(doctype, { diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index 35fec8bc33..aff76eb50f 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -3,6 +3,8 @@ frappe.provide('erpnext.buying'); +{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %}; + frappe.ui.form.on('Subcontracting Receipt', { setup: (frm) => { frm.get_field('supplied_items').grid.cannot_add_rows = true; @@ -128,6 +130,16 @@ frappe.ui.form.on('Subcontracting Receipt', { }, }); +frappe.ui.form.on('Landed Cost Taxes and Charges', { + amount: function (frm, cdt, cdn) { + frm.events.set_base_amount(frm, cdt, cdn); + }, + + expense_account: function (frm, cdt, cdn) { + frm.events.set_account_currency(frm, cdt, cdn); + } +}); + frappe.ui.form.on('Subcontracting Receipt Item', { item_code(frm) { set_missing_values(frm); diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index 9430486560..9221dbfb71 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -47,6 +47,10 @@ "raw_material_details", "get_current_stock", "supplied_items", + "additional_costs_section", + "distribute_additional_costs_based_on", + "additional_costs", + "total_additional_costs", "section_break_46", "in_words", "bill_no", @@ -595,11 +599,39 @@ "fieldtype": "Link", "label": "Project", "options": "Project" + }, + { + "collapsible": 1, + "collapsible_depends_on": "total_additional_costs", + "depends_on": "eval:(doc.docstatus == 0 || doc.total_additional_costs)", + "fieldname": "additional_costs_section", + "fieldtype": "Section Break", + "label": "Additional Costs" + }, + { + "default": "Qty", + "fieldname": "distribute_additional_costs_based_on", + "fieldtype": "Select", + "label": "Distribute Additional Costs Based On ", + "options": "Qty\nAmount" + }, + { + "fieldname": "additional_costs", + "fieldtype": "Table", + "label": "Additional Costs", + "options": "Landed Cost Taxes and Charges" + }, + { + "fieldname": "total_additional_costs", + "fieldtype": "Currency", + "label": "Total Additional Costs", + "print_hide_if_no_value": 1, + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2022-08-15 14:30:29.447307", + "modified": "2022-08-18 15:48:57.419191", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 51007c5635..f8b71ea028 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -103,6 +103,7 @@ class SubcontractingReceipt(SubcontractingController): @frappe.whitelist() def set_missing_values(self): + self.set_missing_values_in_additional_costs() self.set_missing_values_in_supplied_items() self.set_missing_values_in_items() From aafb7352830342ddef3e0cc8cf520b885957098f Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Thu, 18 Aug 2022 15:31:20 +0000 Subject: [PATCH 0068/1047] perf: use `create_custom_fields` (#31853) * perf: use `create_custom_fields` * fix: default must be a string --- .../tally_migration/tally_migration.py | 41 ++++++++------ .../woocommerce_settings.py | 41 +++++++------- erpnext/setup/install.py | 55 +++++++++---------- 3 files changed, 68 insertions(+), 69 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 7d676e4235..cd4aaee2c4 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -12,7 +12,9 @@ from decimal import Decimal import frappe from bs4 import BeautifulSoup as bs from frappe import _ -from frappe.custom.doctype.custom_field.custom_field import create_custom_field +from frappe.custom.doctype.custom_field.custom_field import ( + create_custom_fields as _create_custom_fields, +) from frappe.model.document import Document from frappe.utils.data import format_datetime @@ -577,22 +579,25 @@ class TallyMigration(Document): new_year.save() oldest_year = new_year - def create_custom_fields(doctypes): - tally_guid_df = { - "fieldtype": "Data", - "fieldname": "tally_guid", - "read_only": 1, - "label": "Tally GUID", - } - tally_voucher_no_df = { - "fieldtype": "Data", - "fieldname": "tally_voucher_no", - "read_only": 1, - "label": "Tally Voucher Number", - } - for df in [tally_guid_df, tally_voucher_no_df]: - for doctype in doctypes: - create_custom_field(doctype, df) + def create_custom_fields(): + _create_custom_fields( + { + ("Journal Entry", "Purchase Invoice", "Sales Invoice"): [ + { + "fieldtype": "Data", + "fieldname": "tally_guid", + "read_only": 1, + "label": "Tally GUID", + }, + { + "fieldtype": "Data", + "fieldname": "tally_voucher_no", + "read_only": 1, + "label": "Tally Voucher Number", + }, + ] + } + ) def create_price_list(): frappe.get_doc( @@ -628,7 +633,7 @@ class TallyMigration(Document): create_fiscal_years(vouchers) create_price_list() - create_custom_fields(["Journal Entry", "Purchase Invoice", "Sales Invoice"]) + create_custom_fields() total = len(vouchers) is_last = False diff --git a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py index 2e18776a92..4aa98aab56 100644 --- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py +++ b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py @@ -6,7 +6,7 @@ from urllib.parse import urlparse import frappe from frappe import _ -from frappe.custom.doctype.custom_field.custom_field import create_custom_field +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from frappe.model.document import Document from frappe.utils.nestedset import get_root_of @@ -19,27 +19,24 @@ class WoocommerceSettings(Document): def create_delete_custom_fields(self): if self.enable_sync: - custom_fields = {} - # create - for doctype in ["Customer", "Sales Order", "Item", "Address"]: - df = dict( - fieldname="woocommerce_id", - label="Woocommerce ID", - fieldtype="Data", - read_only=1, - print_hide=1, - ) - create_custom_field(doctype, df) - - for doctype in ["Customer", "Address"]: - df = dict( - fieldname="woocommerce_email", - label="Woocommerce Email", - fieldtype="Data", - read_only=1, - print_hide=1, - ) - create_custom_field(doctype, df) + create_custom_fields( + { + ("Customer", "Sales Order", "Item", "Address"): dict( + fieldname="woocommerce_id", + label="Woocommerce ID", + fieldtype="Data", + read_only=1, + print_hide=1, + ), + ("Customer", "Address"): dict( + fieldname="woocommerce_email", + label="Woocommerce Email", + fieldtype="Data", + read_only=1, + print_hide=1, + ), + } + ) if not frappe.get_value("Item Group", {"name": _("WooCommerce Products")}): item_group = frappe.new_doc("Item Group") diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 7d7e6b5e07..2076dde519 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -4,7 +4,7 @@ import frappe from frappe import _ -from frappe.custom.doctype.custom_field.custom_field import create_custom_field +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to from frappe.utils import cint @@ -83,35 +83,32 @@ def setup_currency_exchange(): def create_print_setting_custom_fields(): - create_custom_field( - "Print Settings", + create_custom_fields( { - "label": _("Compact Item Print"), - "fieldname": "compact_item_print", - "fieldtype": "Check", - "default": 1, - "insert_after": "with_letterhead", - }, - ) - create_custom_field( - "Print Settings", - { - "label": _("Print UOM after Quantity"), - "fieldname": "print_uom_after_quantity", - "fieldtype": "Check", - "default": 0, - "insert_after": "compact_item_print", - }, - ) - create_custom_field( - "Print Settings", - { - "label": _("Print taxes with zero amount"), - "fieldname": "print_taxes_with_zero_amount", - "fieldtype": "Check", - "default": 0, - "insert_after": "allow_print_for_cancelled", - }, + "Print Settings": [ + { + "label": _("Compact Item Print"), + "fieldname": "compact_item_print", + "fieldtype": "Check", + "default": "1", + "insert_after": "with_letterhead", + }, + { + "label": _("Print UOM after Quantity"), + "fieldname": "print_uom_after_quantity", + "fieldtype": "Check", + "default": "0", + "insert_after": "compact_item_print", + }, + { + "label": _("Print taxes with zero amount"), + "fieldname": "print_taxes_with_zero_amount", + "fieldtype": "Check", + "default": "0", + "insert_after": "allow_print_for_cancelled", + }, + ] + } ) From 756fe4b37562b63bbe6c00444bcdeb9b382a5f59 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 19 Aug 2022 10:22:00 +0530 Subject: [PATCH 0069/1047] fix(pos): edge case while closing pos (#31748) * fix(pos): edge case while closing pos * fix: linter * fix: setting posting_time in pos invoice merge log --- .../doctype/pos_closing_entry/pos_closing_entry.js | 9 +++++++++ .../pos_closing_entry/pos_closing_entry.json | 13 +++++++++++-- .../doctype/pos_closing_entry/pos_closing_entry.py | 3 +++ .../pos_invoice_merge_log.json | 11 ++++++++++- .../pos_invoice_merge_log/pos_invoice_merge_log.py | 7 ++++++- 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js index 98f3420d87..1d596c1bfb 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js @@ -36,6 +36,15 @@ frappe.ui.form.on('POS Closing Entry', { }); set_html_data(frm); + + if (frm.doc.docstatus == 1) { + if (!frm.doc.posting_date) { + frm.set_value("posting_date", frappe.datetime.nowdate()); + } + if (!frm.doc.posting_time) { + frm.set_value("posting_time", frappe.datetime.now_time()); + } + } }, refresh: function(frm) { diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json index d6e35c6a50..9d15e6cf35 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json @@ -11,6 +11,7 @@ "period_end_date", "column_break_3", "posting_date", + "posting_time", "pos_opening_entry", "status", "section_break_5", @@ -51,7 +52,6 @@ "fieldtype": "Datetime", "in_list_view": 1, "label": "Period End Date", - "read_only": 1, "reqd": 1 }, { @@ -219,6 +219,13 @@ "fieldtype": "Small Text", "label": "Error", "read_only": 1 + }, + { + "fieldname": "posting_time", + "fieldtype": "Time", + "label": "Posting Time", + "no_copy": 1, + "reqd": 1 } ], "is_submittable": 1, @@ -228,10 +235,11 @@ "link_fieldname": "pos_closing_entry" } ], - "modified": "2021-10-20 16:19:25.340565", + "modified": "2022-08-01 11:37:14.991228", "modified_by": "Administrator", "module": "Accounts", "name": "POS Closing Entry", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { @@ -278,5 +286,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py index 49aab0d0bb..655c4ec003 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py @@ -15,6 +15,9 @@ from erpnext.controllers.status_updater import StatusUpdater class POSClosingEntry(StatusUpdater): def validate(self): + self.posting_date = self.posting_date or frappe.utils.nowdate() + self.posting_time = self.posting_time or frappe.utils.nowtime() + if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open": frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry")) diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json index d762087078..a059455647 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json @@ -6,6 +6,7 @@ "engine": "InnoDB", "field_order": [ "posting_date", + "posting_time", "merge_invoices_based_on", "column_break_3", "pos_closing_entry", @@ -105,12 +106,19 @@ "label": "Customer Group", "mandatory_depends_on": "eval:doc.merge_invoices_based_on == 'Customer Group'", "options": "Customer Group" + }, + { + "fieldname": "posting_time", + "fieldtype": "Time", + "label": "Posting Time", + "no_copy": 1, + "reqd": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-09-14 11:17:19.001142", + "modified": "2022-08-01 11:36:42.456429", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice Merge Log", @@ -173,5 +181,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 5003a1d6a8..81a234a20a 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -9,7 +9,7 @@ from frappe import _ from frappe.core.page.background_jobs.background_jobs import get_info from frappe.model.document import Document from frappe.model.mapper import map_child_doc, map_doc -from frappe.utils import cint, flt, getdate, nowdate +from frappe.utils import cint, flt, get_time, getdate, nowdate, nowtime from frappe.utils.background_jobs import enqueue from frappe.utils.scheduler import is_scheduler_inactive @@ -99,6 +99,7 @@ class POSInvoiceMergeLog(Document): sales_invoice.is_consolidated = 1 sales_invoice.set_posting_time = 1 sales_invoice.posting_date = getdate(self.posting_date) + sales_invoice.posting_time = get_time(self.posting_time) sales_invoice.save() sales_invoice.submit() @@ -115,6 +116,7 @@ class POSInvoiceMergeLog(Document): credit_note.is_consolidated = 1 credit_note.set_posting_time = 1 credit_note.posting_date = getdate(self.posting_date) + credit_note.posting_time = get_time(self.posting_time) # TODO: return could be against multiple sales invoice which could also have been consolidated? # credit_note.return_against = self.consolidated_invoice credit_note.save() @@ -402,6 +404,9 @@ def create_merge_logs(invoice_by_customer, closing_entry=None): merge_log.posting_date = ( getdate(closing_entry.get("posting_date")) if closing_entry else nowdate() ) + merge_log.posting_time = ( + get_time(closing_entry.get("posting_time")) if closing_entry else nowtime() + ) merge_log.customer = customer merge_log.pos_closing_entry = closing_entry.get("name") if closing_entry else None From addd7347d888701f5ff9031a37aeaa25c97dda14 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 19 Aug 2022 11:46:27 +0530 Subject: [PATCH 0070/1047] fix: test "test_pending_and_received_qty" --- .../test_subcontracted_item_to_be_received.py | 25 ++++++++++++------- .../controllers/subcontracting_controller.py | 4 +-- .../subcontracting_order.py | 6 ++--- .../subcontracting_receipt.py | 6 ++--- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py index c772c1a1b1..d13d9701f3 100644 --- a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py +++ b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py @@ -4,6 +4,8 @@ # Decompiled by https://python-decompiler.com +import copy + import frappe from frappe.tests.utils import FrappeTestCase @@ -11,10 +13,12 @@ from erpnext.buying.report.subcontracted_item_to_be_received.subcontracted_item_ execute, ) from erpnext.controllers.tests.test_subcontracting_controller import ( + get_rm_items, get_subcontracting_order, make_service_item, + make_stock_in_entry, + make_stock_transfer_entry, ) -from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import ( make_subcontracting_receipt, ) @@ -36,15 +40,18 @@ class TestSubcontractedItemToBeReceived(FrappeTestCase): sco = get_subcontracting_order( service_items=service_items, supplier_warehouse="_Test Warehouse 1 - _TC" ) - 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, + rm_items = get_rm_items(sco.supplied_items) + itemwise_details = make_stock_in_entry(rm_items=rm_items) + + for item in rm_items: + item["sco_rm_detail"] = sco.items[0].name + + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), ) + make_subcontracting_receipt_against_sco(sco.name) sco.reload() col, data = execute( diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index a944cb8fcf..1372c89d47 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -490,7 +490,7 @@ class SubcontractingController(StockController): row.item_code, row.get(self.subcontract_data.order_field), ) and transfer_item.qty > 0: - qty = self.__get_qty_based_on_material_transfer(row, transfer_item) or 0 + qty = flt(self.__get_qty_based_on_material_transfer(row, transfer_item)) transfer_item.qty -= qty self.__add_supplied_item(row, transfer_item.get("item_details"), qty) @@ -749,7 +749,7 @@ class SubcontractingController(StockController): {"item_code": item.rm_item_code, "warehouse": self.supplier_warehouse}, "actual_qty", ) - item.current_stock = flt(actual_qty) or 0 + item.current_stock = flt(actual_qty) @property def sub_contracted_items(self): diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index 0495fb4174..156f027617 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -95,9 +95,7 @@ class SubcontractingOrder(SubcontractingController): def set_missing_values_in_items(self): total_qty = total = 0 for item in self.items: - item.rate = ( - item.rm_cost_per_qty + item.service_cost_per_qty + (item.additional_cost_per_qty or 0) - ) + item.rate = item.rm_cost_per_qty + item.service_cost_per_qty + flt(item.additional_cost_per_qty) item.amount = item.qty * item.rate total_qty += flt(item.qty) total += flt(item.amount) @@ -168,7 +166,7 @@ class SubcontractingOrder(SubcontractingController): total_required_qty = total_supplied_qty = 0 for item in self.supplied_items: total_required_qty += item.required_qty - total_supplied_qty += item.supplied_qty or 0 + total_supplied_qty += flt(item.supplied_qty) if total_supplied_qty: status = "Partial Material Transferred" if total_supplied_qty >= total_required_qty: diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index f8b71ea028..021d9aa854 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -3,7 +3,7 @@ import frappe from frappe import _ -from frappe.utils import cint, getdate, nowdate +from frappe.utils import cint, flt, getdate, nowdate from erpnext.controllers.subcontracting_controller import SubcontractingController @@ -128,10 +128,10 @@ class SubcontractingReceipt(SubcontractingController): if item.recalculate_rate: item.rate = ( - item.rm_cost_per_qty + (item.service_cost_per_qty or 0) + item.additional_cost_per_qty + flt(item.rm_cost_per_qty) + flt(item.service_cost_per_qty) + flt(item.additional_cost_per_qty) ) - item.received_qty = item.qty + (item.rejected_qty or 0) + item.received_qty = item.qty + flt(item.rejected_qty) item.amount = item.qty * item.rate total_qty += item.qty total_amount += item.amount From c247cf728c6aaaa55a47c8148df807944a515a6e Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 19 Aug 2022 11:10:00 +0530 Subject: [PATCH 0071/1047] chore: add test for additional-cost --- .../tests/test_subcontracting_controller.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py index 4fab8058b8..bc503f5440 100644 --- a/erpnext/controllers/tests/test_subcontracting_controller.py +++ b/erpnext/controllers/tests/test_subcontracting_controller.py @@ -36,6 +36,36 @@ class TestSubcontractingController(FrappeTestCase): sco.remove_empty_rows() self.assertEqual((len_before - 1), len(sco.service_items)) + def test_set_missing_values_in_additional_costs(self): + sco = get_subcontracting_order(do_not_submit=1) + + rate_without_additional_cost = sco.items[0].rate + amount_without_additional_cost = sco.items[0].amount + + additional_amount = 120 + sco.append( + "additional_costs", + { + "expense_account": "Cost of Goods Sold - _TC", + "description": "Test", + "amount": additional_amount, + }, + ) + sco.save() + + additional_cost_per_qty = additional_amount / sco.items[0].qty + + self.assertEqual(sco.items[0].additional_cost_per_qty, additional_cost_per_qty) + self.assertEqual(rate_without_additional_cost + additional_cost_per_qty, sco.items[0].rate) + self.assertEqual(amount_without_additional_cost + additional_amount, sco.items[0].amount) + + sco.additional_costs = [] + sco.save() + + self.assertEqual(sco.items[0].additional_cost_per_qty, 0) + self.assertEqual(rate_without_additional_cost, sco.items[0].rate) + self.assertEqual(amount_without_additional_cost, sco.items[0].amount) + def test_create_raw_materials_supplied(self): sco = get_subcontracting_order() sco.supplied_items = None From f8c11847bbff996d20cc6385d61fe5df3fe75019 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 19 Aug 2022 20:44:13 +0530 Subject: [PATCH 0072/1047] chore: allow subcontracting receipt backdated entry --- .../subcontracting_receipt.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index 3aa8e61ae0..872d18e15e 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -15,6 +15,7 @@ "company", "posting_date", "posting_time", + "set_posting_time", "is_return", "return_against", "accounting_dimensions_section", @@ -138,6 +139,7 @@ "label": "Date", "no_copy": 1, "print_width": "100px", + "read_only_depends_on": "eval: !doc.set_posting_time", "reqd": 1, "search_index": 1, "width": "100px" @@ -150,6 +152,7 @@ "no_copy": 1, "print_hide": 1, "print_width": "100px", + "read_only_depends_on": "eval: !doc.set_posting_time", "reqd": 1, "width": "100px" }, @@ -612,11 +615,19 @@ "label": "Total Additional Costs", "print_hide_if_no_value": 1, "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.docstatus==0", + "fieldname": "set_posting_time", + "fieldtype": "Check", + "label": "Edit Posting Date and Time", + "print_hide": 1 } ], "is_submittable": 1, "links": [], - "modified": "2022-08-18 15:48:57.419191", + "modified": "2022-08-19 19:50:16.935124", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", From f92f3e0208cdfd6812c32a5820f7b08791f64e3c Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 19 Aug 2022 20:52:26 +0530 Subject: [PATCH 0073/1047] chore: add option for "Subcontracting Receipt" in "Voucher Type" --- .../doctype/repost_item_valuation/repost_item_valuation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js index 4cd40bf38e..d6e00eada7 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js @@ -15,7 +15,7 @@ frappe.ui.form.on('Repost Item Valuation', { return { filters: { name: ['in', ['Purchase Receipt', 'Purchase Invoice', 'Delivery Note', - 'Sales Invoice', 'Stock Entry', 'Stock Reconciliation']] + 'Sales Invoice', 'Stock Entry', 'Stock Reconciliation', 'Subcontracting Receipt']] } }; }); From 588ca68171ed8c8f65d9a6c27323e29a2ac30e94 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Sat, 20 Aug 2022 17:50:47 +0530 Subject: [PATCH 0074/1047] fix: make rate field read-only in subcontracting receipt item (#31905) --- .../subcontracting_receipt_item.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json index dcde635992..fd86895b9e 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -194,6 +194,8 @@ "label": "Rate", "options": "currency", "print_width": "100px", + "read_only": 1, + "read_only_depends_on": "eval: doc.recalculate_rate", "width": "100px" }, { @@ -472,7 +474,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-08-18 19:42:24.313449", + "modified": "2022-08-20 17:16:48.269164", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item", From 520306dc8751ab39cee7675a481b00e6dfeaa1b0 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Sun, 21 Aug 2022 12:09:08 +0530 Subject: [PATCH 0075/1047] fix: Add docstatus filter for voucher_no in Repost Item Valuation --- .../doctype/repost_item_valuation/repost_item_valuation.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js index d6e00eada7..eae73050b2 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js @@ -24,7 +24,8 @@ frappe.ui.form.on('Repost Item Valuation', { frm.set_query("voucher_no", () => { return { filters: { - company: frm.doc.company + company: frm.doc.company, + docstatus: 1 } }; }); From 3b15966cc9fb2cf91eca9105f767d8775845f7fa Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 21 Aug 2022 17:51:05 +0530 Subject: [PATCH 0076/1047] fix: Cash and non trade discount calculation --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 2 +- .../doctype/sales_invoice/sales_invoice.js | 4 ++ .../doctype/sales_invoice/sales_invoice.py | 16 ------- erpnext/controllers/accounts_controller.py | 46 ++++++++++--------- erpnext/controllers/taxes_and_totals.py | 16 ++++--- .../public/js/controllers/taxes_and_totals.js | 10 ++++ 6 files changed, 49 insertions(+), 45 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 1987c8340d..7227b95818 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -366,7 +366,7 @@ def update_outstanding_amt( if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]: ref_doc = frappe.get_doc(against_voucher_type, against_voucher) - # Didn't use db_set for optimisation purpose + # Didn't use db_set for optimization purpose ref_doc.outstanding_amount = bal frappe.db.set_value(against_voucher_type, against_voucher, "outstanding_amount", bal) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index adcbb83e8d..73ec051c6d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -479,9 +479,13 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e is_cash_or_non_trade_discount() { this.frm.set_df_property("additional_discount_account", "hidden", 1 - this.frm.doc.is_cash_or_non_trade_discount); + this.frm.set_df_property("additional_discount_account", "reqd", this.frm.doc.is_cash_or_non_trade_discount); + if (!this.frm.doc.is_cash_or_non_trade_discount) { this.frm.set_value("additional_discount_account", ""); } + + this.calculate_taxes_and_totals(); } }; diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 19a234d9df..4008863e9b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1033,22 +1033,6 @@ class SalesInvoice(SellingController): ) ) - if self.apply_discount_on == "Grand Total" and self.get("is_cash_or_discount_account"): - gl_entries.append( - self.get_gl_dict( - { - "account": self.additional_discount_account, - "against": self.debit_to, - "debit": self.base_discount_amount, - "debit_in_account_currency": self.discount_amount, - "cost_center": self.cost_center, - "project": self.project, - }, - self.currency, - item=self, - ) - ) - def make_tax_gl_entries(self, gl_entries): enable_discount_accounting = cint( frappe.db.get_single_value("Selling Settings", "enable_discount_accounting") diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 70d2bc68a1..28070d2377 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1109,17 +1109,17 @@ class AccountsController(TransactionBase): frappe.db.get_single_value("Selling Settings", "enable_discount_accounting") ) + if self.doctype == "Purchase Invoice": + dr_or_cr = "credit" + rev_dr_cr = "debit" + supplier_or_customer = self.supplier + + else: + dr_or_cr = "debit" + rev_dr_cr = "credit" + supplier_or_customer = self.customer + if enable_discount_accounting: - if self.doctype == "Purchase Invoice": - dr_or_cr = "credit" - rev_dr_cr = "debit" - supplier_or_customer = self.supplier - - else: - dr_or_cr = "debit" - rev_dr_cr = "credit" - supplier_or_customer = self.customer - for item in self.get("items"): if item.get("discount_amount") and item.get("discount_account"): discount_amount = item.discount_amount * item.qty @@ -1173,18 +1173,22 @@ class AccountsController(TransactionBase): ) ) - if self.get("discount_amount") and self.get("additional_discount_account"): - gl_entries.append( - self.get_gl_dict( - { - "account": self.additional_discount_account, - "against": supplier_or_customer, - dr_or_cr: self.discount_amount, - "cost_center": self.cost_center, - }, - item=self, - ) + if ( + (enable_discount_accounting or self.is_cash_or_non_trade_discount) + and self.get("additional_discount_account") + and self.get("discount_amount") + ): + gl_entries.append( + self.get_gl_dict( + { + "account": self.additional_discount_account, + "against": supplier_or_customer, + dr_or_cr: self.discount_amount, + "cost_center": self.cost_center, + }, + item=self, ) + ) def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield): from erpnext.controllers.status_updater import get_allowance_for diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 0d8cffe03f..bc38d08b80 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -37,6 +37,11 @@ class calculate_taxes_and_totals(object): self.set_discount_amount() self.apply_discount_amount() + # Update grand total as per cash and non trade discount + if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"): + self.doc.grand_total -= self.doc.discount_amount + self.doc.base_grand_total -= self.doc.base_discount_amount + self.calculate_shipping_charges() if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]: @@ -500,9 +505,6 @@ class calculate_taxes_and_totals(object): else: self.doc.grand_total = flt(self.doc.net_total) - if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"): - self.doc.grand_total -= self.doc.discount_amount - if self.doc.get("taxes"): self.doc.total_taxes_and_charges = flt( self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment), @@ -597,16 +599,16 @@ class calculate_taxes_and_totals(object): if not self.doc.apply_discount_on: frappe.throw(_("Please select Apply Discount On")) + self.doc.base_discount_amount = flt( + self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount") + ) + if self.doc.apply_discount_on == "Grand Total" and self.doc.get( "is_cash_or_non_trade_discount" ): self.discount_amount_applied = True return - self.doc.base_discount_amount = flt( - self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount") - ) - total_for_discount_amount = self.get_total_for_discount_amount() taxes = self.doc.get("taxes") net_total = 0 diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 16b0b4a866..3b9e23a3f5 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -39,6 +39,12 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { this._calculate_taxes_and_totals(); this.calculate_discount_amount(); + // # Update grand total as per cash and non trade discount + if (this.frm.doc.apply_discount_on == "Grand Total" && this.frm.doc.is_cash_or_non_trade_discount) { + this.frm.doc.grand_total -= this.frm.doc.discount_amount; + this.frm.doc.base_grand_total -= this.frm.doc.base_discount_amount; + } + await this.calculate_shipping_charges(); // Advance calculation applicable to Sales /Purchase Invoice @@ -633,6 +639,10 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { this.frm.doc.base_discount_amount = flt(this.frm.doc.discount_amount * this.frm.doc.conversion_rate, precision("base_discount_amount")); + if (this.frm.doc.apply_discount_on == "Grand Total" && this.frm.doc.is_cash_or_non_trade_discount) { + return + } + var total_for_discount_amount = this.get_total_for_discount_amount(); var net_total = 0; // calculate item amount after Discount Amount From f4673941e00f153c87acb08183af2c5c8cea0883 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Sun, 21 Aug 2022 21:26:06 +0530 Subject: [PATCH 0077/1047] chore: move function "add_gl_entry" from purchase_receipt.py to stock_controller.py --- erpnext/controllers/stock_controller.py | 41 +++++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 41 ------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 36bed36484..2e526af658 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -688,6 +688,47 @@ class StockController(AccountsController): else: create_repost_item_valuation_entry(args) + def add_gl_entry( + self, + gl_entries, + account, + cost_center, + debit, + credit, + remarks, + against_account, + debit_in_account_currency=None, + credit_in_account_currency=None, + account_currency=None, + project=None, + voucher_detail_no=None, + item=None, + posting_date=None, + ): + + gl_entry = { + "account": account, + "cost_center": cost_center, + "debit": debit, + "credit": credit, + "against": against_account, + "remarks": remarks, + } + + if voucher_detail_no: + gl_entry.update({"voucher_detail_no": voucher_detail_no}) + + if debit_in_account_currency: + gl_entry.update({"debit_in_account_currency": debit_in_account_currency}) + + if credit_in_account_currency: + gl_entry.update({"credit_in_account_currency": credit_in_account_currency}) + + if posting_date: + gl_entry.update({"posting_date": posting_date}) + + gl_entries.append(self.get_gl_dict(gl_entry, item=item)) + def repost_required_for_queue(doc: StockController) -> bool: """check if stock document contains repeated item-warehouse with queue based valuation. diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 84da3cc41d..51d914dc62 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -631,47 +631,6 @@ class PurchaseReceipt(BuyingController): i += 1 - def add_gl_entry( - self, - gl_entries, - account, - cost_center, - debit, - credit, - remarks, - against_account, - debit_in_account_currency=None, - credit_in_account_currency=None, - account_currency=None, - project=None, - voucher_detail_no=None, - item=None, - posting_date=None, - ): - - gl_entry = { - "account": account, - "cost_center": cost_center, - "debit": debit, - "credit": credit, - "against": against_account, - "remarks": remarks, - } - - if voucher_detail_no: - gl_entry.update({"voucher_detail_no": voucher_detail_no}) - - if debit_in_account_currency: - gl_entry.update({"debit_in_account_currency": debit_in_account_currency}) - - if credit_in_account_currency: - gl_entry.update({"credit_in_account_currency": credit_in_account_currency}) - - if posting_date: - gl_entry.update({"posting_date": posting_date}) - - gl_entries.append(self.get_gl_dict(gl_entry, item=item)) - def get_asset_gl_entry(self, gl_entries): for item in self.get("items"): if item.is_fixed_asset: From ae3dce0cbdca40294739b547d85bfca9b1e28c0f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 22 Aug 2022 08:57:58 +0530 Subject: [PATCH 0078/1047] fix: Test cases --- 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 28070d2377..e689d567a1 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1174,7 +1174,7 @@ class AccountsController(TransactionBase): ) if ( - (enable_discount_accounting or self.is_cash_or_non_trade_discount) + (enable_discount_accounting or self.get("is_cash_or_non_trade_discount")) and self.get("additional_discount_account") and self.get("discount_amount") ): From 42de9ca49e6d615b58494930d7373327ce81a2d2 Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Fri, 19 Aug 2022 12:30:47 +0530 Subject: [PATCH 0079/1047] fix: TDS calculation for advance payment "against_voucher": ["is", "not set"] was used in query due to which if TDS was added on "advance" payment vouchers and then reconciled against purchase invoice. it will not find those vouchers and consider this as first-time threshold due to which it will calculate Tax for all transactions. (cherry picked from commit a4521437825a960e14556fa3963bd1bd1a55a2dc) --- .../doctype/tax_withholding_category/tax_withholding_category.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index a519d8be73..6004e2b19b 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -318,7 +318,6 @@ def get_advance_vouchers( "is_cancelled": 0, "party_type": party_type, "party": ["in", parties], - "against_voucher": ["is", "not set"], } if company: From e888639c7ed175c53a26a38ffa9a22775332096b Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Mon, 22 Aug 2022 10:48:21 +0530 Subject: [PATCH 0080/1047] fix: Subcontracting Receipt GL Entries --- erpnext/patches.txt | 3 +- .../fix_subcontracting_receipt_gl_entries.py | 30 ++++ .../subcontracting_receipt.py | 133 ++++++++++++++++++ 3 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v14_0/fix_subcontracting_receipt_gl_entries.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d92353aca4..30868298b6 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -311,4 +311,5 @@ erpnext.patches.v14_0.remove_india_localisation # 14-07-2022 erpnext.patches.v13_0.fix_number_and_frequency_for_monthly_depreciation erpnext.patches.v14_0.remove_hr_and_payroll_modules # 20-07-2022 erpnext.patches.v14_0.fix_crm_no_of_employees -erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes \ No newline at end of file +erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes +erpnext.patches.v14_0.fix_subcontracting_receipt_gl_entries \ No newline at end of file diff --git a/erpnext/patches/v14_0/fix_subcontracting_receipt_gl_entries.py b/erpnext/patches/v14_0/fix_subcontracting_receipt_gl_entries.py new file mode 100644 index 0000000000..159c6dc82d --- /dev/null +++ b/erpnext/patches/v14_0/fix_subcontracting_receipt_gl_entries.py @@ -0,0 +1,30 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe + +from erpnext.stock.report.stock_and_account_value_comparison.stock_and_account_value_comparison import ( + get_data, +) + + +def execute(): + data = [] + + for company in frappe.db.get_list("Company", pluck="name"): + data += get_data( + frappe._dict( + { + "company": company, + } + ) + ) + + if data: + for d in data: + if d and d.get("voucher_type") == "Subcontracting Receipt": + doc = frappe.new_doc("Repost Item Valuation") + doc.voucher_type = d.get("voucher_type") + doc.voucher_no = d.get("voucher_no") + doc.save() + doc.submit() diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 021d9aa854..b8134d7b27 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -5,6 +5,8 @@ import frappe from frappe import _ from frappe.utils import cint, flt, getdate, nowdate +import erpnext +from erpnext.accounts.utils import get_account_currency from erpnext.controllers.subcontracting_controller import SubcontractingController @@ -181,6 +183,137 @@ class SubcontractingReceipt(SubcontractingController): if status: frappe.db.set_value("Subcontracting Receipt", self.name, "status", status, update_modified) + def get_gl_entries(self, warehouse_account=None): + from erpnext.accounts.general_ledger import process_gl_map + + gl_entries = [] + self.make_item_gl_entries(gl_entries, warehouse_account) + + return process_gl_map(gl_entries) + + def make_item_gl_entries(self, gl_entries, warehouse_account=None): + if erpnext.is_perpetual_inventory_enabled(self.company): + stock_rbnb = self.get_company_default("stock_received_but_not_billed") + expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") + + warehouse_with_no_account = [] + + for item in self.items: + if flt(item.rate) and flt(item.qty): + if warehouse_account.get(item.warehouse): + stock_value_diff = frappe.db.get_value( + "Stock Ledger Entry", + { + "voucher_type": "Subcontracting Receipt", + "voucher_no": self.name, + "voucher_detail_no": item.name, + "warehouse": item.warehouse, + "is_cancelled": 0, + }, + "stock_value_difference", + ) + + warehouse_account_name = warehouse_account[item.warehouse]["account"] + warehouse_account_currency = warehouse_account[item.warehouse]["account_currency"] + supplier_warehouse_account = warehouse_account.get(self.supplier_warehouse, {}).get("account") + supplier_warehouse_account_currency = warehouse_account.get(self.supplier_warehouse, {}).get( + "account_currency" + ) + remarks = self.get("remarks") or _("Accounting Entry for Stock") + + # FG Warehouse Account (Debit) + self.add_gl_entry( + gl_entries=gl_entries, + account=warehouse_account_name, + cost_center=item.cost_center, + debit=stock_value_diff, + credit=0.0, + remarks=remarks, + against_account=stock_rbnb, + account_currency=warehouse_account_currency, + item=item, + ) + + # Supplier Warehouse Account (Credit) + if flt(item.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse): + self.add_gl_entry( + gl_entries=gl_entries, + account=supplier_warehouse_account, + cost_center=item.cost_center, + debit=0.0, + credit=flt(item.rm_supp_cost), + remarks=remarks, + against_account=warehouse_account_name, + account_currency=supplier_warehouse_account_currency, + item=item, + ) + + # Expense Account (Credit) + if flt(item.service_cost_per_qty): + self.add_gl_entry( + gl_entries=gl_entries, + account=item.expense_account, + cost_center=item.cost_center, + debit=0.0, + credit=flt(item.service_cost_per_qty) * flt(item.qty), + remarks=remarks, + against_account=warehouse_account_name, + account_currency=get_account_currency(item.expense_account), + item=item, + ) + + # Loss Account (Credit) + divisional_loss = flt(item.amount - stock_value_diff, item.precision("amount")) + + if divisional_loss: + if self.is_return: + loss_account = expenses_included_in_valuation + else: + loss_account = item.expense_account + + self.add_gl_entry( + gl_entries=gl_entries, + account=loss_account, + cost_center=item.cost_center, + debit=divisional_loss, + credit=0.0, + remarks=remarks, + against_account=warehouse_account_name, + account_currency=get_account_currency(loss_account), + project=item.project, + item=item, + ) + elif ( + item.warehouse not in warehouse_with_no_account + or item.rejected_warehouse not in warehouse_with_no_account + ): + warehouse_with_no_account.append(item.warehouse) + + # Additional Costs Expense Accounts (Credit) + for row in self.additional_costs: + credit_amount = ( + flt(row.base_amount) + if (row.base_amount or row.account_currency != self.company_currency) + else flt(row.amount) + ) + + self.add_gl_entry( + gl_entries=gl_entries, + account=row.expense_account, + cost_center=self.cost_center or self.get_company_default("cost_center"), + debit=0.0, + credit=credit_amount, + remarks=remarks, + against_account=None, + ) + + if warehouse_with_no_account: + frappe.msgprint( + _("No accounting entries for the following warehouses") + + ": \n" + + "\n".join(warehouse_with_no_account) + ) + @frappe.whitelist() def make_subcontract_return(source_name, target_doc=None): From bf5c43322a2fdf299a031ee2b864f5da0d9e15a9 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Mon, 22 Aug 2022 18:36:42 +0530 Subject: [PATCH 0081/1047] fix: don't allow to create SCR directly (#31924) --- .../doctype/subcontracting_receipt/subcontracting_receipt.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index 872d18e15e..84e95548e1 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -625,9 +625,10 @@ "print_hide": 1 } ], + "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2022-08-19 19:50:16.935124", + "modified": "2022-08-22 17:30:40.827517", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", From 2effbb55ae6b2b5f2295fb19c4071a7cf70955a8 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Mon, 22 Aug 2022 22:13:28 +0530 Subject: [PATCH 0082/1047] test: Add test case for Subcontracting Receipt GL Entries --- .../tests/test_subcontracting_controller.py | 7 +- .../test_subcontracting_order.py | 2 +- .../test_subcontracting_receipt.py | 102 +++++++++++++++++- 3 files changed, 106 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py index bc503f5440..8490d14528 100644 --- a/erpnext/controllers/tests/test_subcontracting_controller.py +++ b/erpnext/controllers/tests/test_subcontracting_controller.py @@ -897,7 +897,7 @@ def make_stock_transfer_entry(**args): "item_name": row.item_code, "rate": row.rate or 100, "stock_uom": row.stock_uom or "Nos", - "warehouse": row.warehuose or "_Test Warehouse - _TC", + "warehouse": row.warehouse or "_Test Warehouse - _TC", } item_details = args.itemwise_details.get(row.item_code) @@ -1031,9 +1031,9 @@ def get_subcontracting_order(**args): if not args.service_items: service_items = [ { - "warehouse": "_Test Warehouse - _TC", + "warehouse": args.warehouse or "_Test Warehouse - _TC", "item_code": "Subcontracted Service Item 7", - "qty": 5, + "qty": 10, "rate": 100, "fg_item": "Subcontracted Item SA7", "fg_item_qty": 10, @@ -1046,6 +1046,7 @@ def get_subcontracting_order(**args): rm_items=service_items, is_subcontracted=1, supplier_warehouse=args.supplier_warehouse or "_Test Warehouse 1 - _TC", + company=args.company, ) return create_subcontracting_order(po_name=po.name, **args) diff --git a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py index 94bb38e980..a676e48fad 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py @@ -511,7 +511,7 @@ def create_subcontracting_order(**args): for item in sco.items: item.include_exploded_items = args.get("include_exploded_items", 1) - if args.get("warehouse"): + if args.warehouse: for item in sco.items: item.warehouse = args.warehouse else: diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 763e76882e..b5e6139bb7 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -6,8 +6,10 @@ import copy import frappe from frappe.tests.utils import FrappeTestCase -from frappe.utils import flt +from frappe.utils import cint, flt +import erpnext +from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.controllers.sales_and_purchase_return import make_return_doc from erpnext.controllers.tests.test_subcontracting_controller import ( get_rm_items, @@ -22,6 +24,7 @@ from erpnext.controllers.tests.test_subcontracting_controller import ( set_backflush_based_on, ) from erpnext.stock.doctype.item.test_item import make_item +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import ( make_subcontracting_receipt, @@ -317,6 +320,103 @@ class TestSubcontractingReceipt(FrappeTestCase): args = frappe._dict(scr_name=scr1.name, qty=-15) self.assertRaises(OverAllowanceError, make_return_subcontracting_receipt, **args) + def test_subcontracting_receipt_no_gl_entry(self): + sco = get_subcontracting_order() + rm_items = get_rm_items(sco.supplied_items) + itemwise_details = make_stock_in_entry(rm_items=rm_items) + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + + scr = make_subcontracting_receipt(sco.name) + scr.append( + "additional_costs", + { + "expense_account": "Expenses Included In Valuation - _TC", + "description": "Test Additional Costs", + "amount": 100, + }, + ) + scr.save() + scr.submit() + + stock_value_difference = frappe.db.get_value( + "Stock Ledger Entry", + { + "voucher_type": "Subcontracting Receipt", + "voucher_no": scr.name, + "item_code": "Subcontracted Item SA7", + "warehouse": "_Test Warehouse - _TC", + }, + "stock_value_difference", + ) + + # Service Cost(100 * 10) + Raw Materials Cost(50 * 10) + Additional Costs(100) = 1600 + self.assertEqual(stock_value_difference, 1600) + self.assertFalse(get_gl_entries("Subcontracting Receipt", scr.name)) + + def test_subcontracting_receipt_gl_entry(self): + sco = get_subcontracting_order( + company="_Test Company with perpetual inventory", + warehouse="Stores - TCP1", + supplier_warehouse="Work In Progress - TCP1", + ) + rm_items = get_rm_items(sco.supplied_items) + itemwise_details = make_stock_in_entry(rm_items=rm_items) + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + + scr = make_subcontracting_receipt(sco.name) + additional_costs_expense_account = "Expenses Included In Valuation - TCP1" + scr.append( + "additional_costs", + { + "expense_account": additional_costs_expense_account, + "description": "Test Additional Costs", + "amount": 100, + "base_amount": 100, + }, + ) + scr.save() + scr.submit() + + self.assertEqual(cint(erpnext.is_perpetual_inventory_enabled(scr.company)), 1) + + gl_entries = get_gl_entries("Subcontracting Receipt", scr.name) + + self.assertTrue(gl_entries) + + fg_warehouse_ac = get_inventory_account(scr.company, scr.items[0].warehouse) + supplier_warehouse_ac = get_inventory_account(scr.company, scr.supplier_warehouse) + expense_account = scr.items[0].expense_account + + if fg_warehouse_ac == supplier_warehouse_ac: + expected_values = { + fg_warehouse_ac: [2100.0, 1000.0], # FG Amount (D), RM Cost (C) + expense_account: [0.0, 1000.0], # Service Cost (C) + additional_costs_expense_account: [0.0, 100.0], # Additional Cost (C) + } + else: + expected_values = { + fg_warehouse_ac: [2100.0, 0.0], # FG Amount (D) + supplier_warehouse_ac: [0.0, 1000.0], # RM Cost (C) + expense_account: [0.0, 1000.0], # Service Cost (C) + additional_costs_expense_account: [0.0, 100.0], # Additional Cost (C) + } + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account][0], gle.debit) + self.assertEqual(expected_values[gle.account][1], gle.credit) + + scr.reload() + scr.cancel() + self.assertTrue(get_gl_entries("Subcontracting Receipt", scr.name)) + def make_return_subcontracting_receipt(**args): args = frappe._dict(args) From 1cb7ae16ab156da2fd43ec915316947b0b8db964 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 23 Aug 2022 09:12:20 +0530 Subject: [PATCH 0083/1047] chore: Linting issues --- erpnext/public/js/controllers/taxes_and_totals.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 3b9e23a3f5..4c3e9dcf0a 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -640,7 +640,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { precision("base_discount_amount")); if (this.frm.doc.apply_discount_on == "Grand Total" && this.frm.doc.is_cash_or_non_trade_discount) { - return + return; } var total_for_discount_amount = this.get_total_for_discount_amount(); From a956e20f294a5d66d6589c479398eefc632eff43 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 23 Aug 2022 11:36:00 +0530 Subject: [PATCH 0084/1047] refactor: disable discount accounting on Buying module(PI) --- .../accounts/doctype/purchase_invoice/purchase_invoice.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index de3927e45b..1d2a5d6c27 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -575,7 +575,6 @@ class PurchaseInvoice(BuyingController): self.make_supplier_gl_entry(gl_entries) self.make_item_gl_entries(gl_entries) - self.make_discount_gl_entries(gl_entries) if self.check_asset_cwip_enabled(): self.get_asset_gl_entry(gl_entries) @@ -807,7 +806,7 @@ class PurchaseInvoice(BuyingController): ) if not item.is_fixed_asset: - dummy, amount = self.get_amount_and_base_amount(item, enable_discount_accounting) + dummy, amount = self.get_amount_and_base_amount(item, None) else: amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount")) @@ -1165,7 +1164,7 @@ class PurchaseInvoice(BuyingController): ) for tax in self.get("taxes"): - amount, base_amount = self.get_tax_amounts(tax, enable_discount_accounting) + amount, base_amount = self.get_tax_amounts(tax, None) if tax.category in ("Total", "Valuation and Total") and flt(base_amount): account_currency = get_account_currency(tax.account_head) From 277ef04b600412fa8cc60abc90ba254c35b2ecf6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 23 Aug 2022 15:17:27 +0530 Subject: [PATCH 0085/1047] test: remove discount accounting tests --- .../purchase_invoice/test_purchase_invoice.py | 53 ------------------- 1 file changed, 53 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index e55d3a70af..0a4f25b876 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -338,59 +338,6 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount) - @change_settings("Buying Settings", {"enable_discount_accounting": 1}) - def test_purchase_invoice_with_discount_accounting_enabled(self): - - discount_account = create_account( - account_name="Discount Account", - parent_account="Indirect Expenses - _TC", - company="_Test Company", - ) - pi = make_purchase_invoice(discount_account=discount_account, rate=45) - - expected_gle = [ - ["_Test Account Cost for Goods Sold - _TC", 250.0, 0.0, nowdate()], - ["Creditors - _TC", 0.0, 225.0, nowdate()], - ["Discount Account - _TC", 0.0, 25.0, nowdate()], - ] - - check_gl_entries(self, pi.name, expected_gle, nowdate()) - - @change_settings("Buying Settings", {"enable_discount_accounting": 1}) - def test_additional_discount_for_purchase_invoice_with_discount_accounting_enabled(self): - - additional_discount_account = create_account( - account_name="Discount Account", - parent_account="Indirect Expenses - _TC", - company="_Test Company", - ) - - pi = make_purchase_invoice(do_not_save=1, parent_cost_center="Main - _TC") - pi.apply_discount_on = "Grand Total" - pi.additional_discount_account = additional_discount_account - pi.additional_discount_percentage = 10 - pi.disable_rounded_total = 1 - pi.append( - "taxes", - { - "charge_type": "On Net Total", - "account_head": "_Test Account VAT - _TC", - "cost_center": "Main - _TC", - "description": "Test", - "rate": 10, - }, - ) - pi.submit() - - expected_gle = [ - ["_Test Account Cost for Goods Sold - _TC", 250.0, 0.0, nowdate()], - ["_Test Account VAT - _TC", 25.0, 0.0, nowdate()], - ["Creditors - _TC", 0.0, 247.5, nowdate()], - ["Discount Account - _TC", 0.0, 27.5, nowdate()], - ] - - check_gl_entries(self, pi.name, expected_gle, nowdate()) - def test_purchase_invoice_change_naming_series(self): pi = frappe.copy_doc(test_records[1]) pi.insert() From fe73d55f702163386e413b61dd504f93a8e8479c Mon Sep 17 00:00:00 2001 From: HENRY Florian Date: Tue, 23 Aug 2022 12:37:10 +0200 Subject: [PATCH 0086/1047] chore: add Work Order test dependencies (#31936) --- erpnext/manufacturing/doctype/work_order/test_work_order.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index b556d9974a..a53c42c5ec 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -26,6 +26,8 @@ from erpnext.stock.doctype.stock_entry import test_stock_entry from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse from erpnext.stock.utils import get_bin +test_dependencies = ["BOM"] + class TestWorkOrder(FrappeTestCase): def setUp(self): From fdd167cac123a6f2fea11ccdd73f3b12a65f2f8d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 24 Aug 2022 12:24:55 +0530 Subject: [PATCH 0087/1047] fix: include payment against PO in AR/AP report --- .../report/accounts_receivable/accounts_receivable.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index e937edbeb2..eb2959e197 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -178,6 +178,11 @@ class ReceivablePayableReport(object): key = (ple.against_voucher_type, ple.against_voucher_no, ple.party) row = self.voucher_balance.get(key) + + if not row: + # no invoice, this is an invoice / stand-alone payment / credit note + row = self.voucher_balance.get((ple.voucher_type, ple.voucher_no, ple.party)) + return row def update_voucher_balance(self, ple): From b4a2eb2e65b0706ba7483215040855297b9b9ff8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 24 Aug 2022 12:29:15 +0530 Subject: [PATCH 0088/1047] fix: gl entries for asset repair --- erpnext/assets/doctype/asset/test_asset.py | 14 +- .../doctype/asset_repair/asset_repair.js | 2 +- .../doctype/asset_repair/asset_repair.json | 6 +- .../doctype/asset_repair/asset_repair.py | 146 +++++++++++------- .../doctype/asset_repair/test_asset_repair.py | 122 ++++++++++++++- erpnext/setup/doctype/company/company.json | 10 +- 6 files changed, 222 insertions(+), 78 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 986b7001ff..132840e38c 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -1454,12 +1454,14 @@ def create_fixed_asset_item(item_code=None, auto_create_assets=1, is_grouped_ass return item -def set_depreciation_settings_in_company(): - company = frappe.get_doc("Company", "_Test Company") - company.accumulated_depreciation_account = "_Test Accumulated Depreciations - _TC" - company.depreciation_expense_account = "_Test Depreciations - _TC" - company.disposal_account = "_Test Gain/Loss on Asset Disposal - _TC" - company.depreciation_cost_center = "_Test Cost Center - _TC" +def set_depreciation_settings_in_company(company=None): + if not company: + company = "_Test Company" + company = frappe.get_doc("Company", company) + company.accumulated_depreciation_account = "_Test Accumulated Depreciations - " + company.abbr + company.depreciation_expense_account = "_Test Depreciations - " + company.abbr + company.disposal_account = "_Test Gain/Loss on Asset Disposal - " + company.abbr + company.depreciation_cost_center = "Main - " + company.abbr company.save() # Enable booking asset depreciation entry automatically diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index f5e4e723b4..f9ed2cc344 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -76,7 +76,7 @@ frappe.ui.form.on('Asset Repair Consumed Item', { 'warehouse': frm.doc.warehouse, 'qty': item.consumed_quantity, 'serial_no': item.serial_no, - 'company': frm.doc.company + 'company': frm.doc.company, }; frappe.call({ diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index ba3189887c..accb5bf54b 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -238,7 +238,6 @@ "no_copy": 1 }, { - "depends_on": "eval:!doc.__islocal", "fieldname": "purchase_invoice", "fieldtype": "Link", "label": "Purchase Invoice", @@ -257,6 +256,7 @@ "fieldname": "stock_entry", "fieldtype": "Link", "label": "Stock Entry", + "no_copy": 1, "options": "Stock Entry", "read_only": 1 } @@ -264,10 +264,11 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-25 13:14:38.307723", + "modified": "2022-08-16 15:55:25.023471", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { @@ -303,6 +304,7 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "title_field": "asset_name", "track_changes": 1, "track_seen": 1 diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 5bf6011cf8..b4316ced62 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -1,11 +1,11 @@ # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt - import frappe from frappe import _ from frappe.utils import add_months, cint, flt, getdate, time_diff_in_hours +import erpnext from erpnext.accounts.general_ledger import make_gl_entries from erpnext.assets.doctype.asset.asset import get_asset_account from erpnext.controllers.accounts_controller import AccountsController @@ -17,7 +17,7 @@ class AssetRepair(AccountsController): self.update_status() if self.get("stock_items"): - self.set_total_value() + self.set_stock_items_cost() self.calculate_total_repair_cost() def update_status(self): @@ -26,7 +26,7 @@ class AssetRepair(AccountsController): else: self.asset_doc.set_status() - def set_total_value(self): + def set_stock_items_cost(self): for item in self.get("stock_items"): item.total_value = flt(item.valuation_rate) * flt(item.consumed_quantity) @@ -66,6 +66,7 @@ class AssetRepair(AccountsController): if self.get("capitalize_repair_cost"): self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry") self.make_gl_entries(cancel=True) + self.db_set("stock_entry", None) if ( frappe.db.get_value("Asset", self.asset, "calculate_depreciation") and self.increase_in_asset_life @@ -133,6 +134,7 @@ class AssetRepair(AccountsController): "qty": stock_item.consumed_quantity, "basic_rate": stock_item.valuation_rate, "serial_no": stock_item.serial_no, + "cost_center": self.cost_center, }, ) @@ -142,72 +144,42 @@ class AssetRepair(AccountsController): self.db_set("stock_entry", stock_entry.name) def increase_stock_quantity(self): - stock_entry = frappe.get_doc("Stock Entry", self.stock_entry) - stock_entry.flags.ignore_links = True - stock_entry.cancel() + if self.stock_entry: + stock_entry = frappe.get_doc("Stock Entry", self.stock_entry) + stock_entry.flags.ignore_links = True + stock_entry.cancel() def make_gl_entries(self, cancel=False): - if flt(self.repair_cost) > 0: + if flt(self.total_repair_cost) > 0: gl_entries = self.get_gl_entries() make_gl_entries(gl_entries, cancel) def get_gl_entries(self): gl_entries = [] - repair_and_maintenance_account = frappe.db.get_value( - "Company", self.company, "repair_and_maintenance_account" - ) + fixed_asset_account = get_asset_account( "fixed_asset_account", asset=self.asset, company=self.company ) - expense_account = ( + self.get_gl_entries_for_repair_cost(gl_entries, fixed_asset_account) + self.get_gl_entries_for_consumed_items(gl_entries, fixed_asset_account) + + return gl_entries + + def get_gl_entries_for_repair_cost(self, gl_entries, fixed_asset_account): + if flt(self.repair_cost) <= 0: + return + + pi_expense_account = ( frappe.get_doc("Purchase Invoice", self.purchase_invoice).items[0].expense_account ) - gl_entries.append( - self.get_gl_dict( - { - "account": expense_account, - "credit": self.repair_cost, - "credit_in_account_currency": self.repair_cost, - "against": repair_and_maintenance_account, - "voucher_type": self.doctype, - "voucher_no": self.name, - "cost_center": self.cost_center, - "posting_date": getdate(), - "company": self.company, - }, - item=self, - ) - ) - - if self.get("stock_consumption"): - # creating GL Entries for each row in Stock Items based on the Stock Entry created for it - stock_entry = frappe.get_doc("Stock Entry", self.stock_entry) - for item in stock_entry.items: - gl_entries.append( - self.get_gl_dict( - { - "account": item.expense_account, - "credit": item.amount, - "credit_in_account_currency": item.amount, - "against": repair_and_maintenance_account, - "voucher_type": self.doctype, - "voucher_no": self.name, - "cost_center": self.cost_center, - "posting_date": getdate(), - "company": self.company, - }, - item=self, - ) - ) - gl_entries.append( self.get_gl_dict( { "account": fixed_asset_account, - "debit": self.total_repair_cost, - "debit_in_account_currency": self.total_repair_cost, - "against": expense_account, + "debit": self.repair_cost, + "debit_in_account_currency": self.repair_cost, + "against": pi_expense_account, "voucher_type": self.doctype, "voucher_no": self.name, "cost_center": self.cost_center, @@ -220,7 +192,75 @@ class AssetRepair(AccountsController): ) ) - return gl_entries + gl_entries.append( + self.get_gl_dict( + { + "account": pi_expense_account, + "credit": self.repair_cost, + "credit_in_account_currency": self.repair_cost, + "against": fixed_asset_account, + "voucher_type": self.doctype, + "voucher_no": self.name, + "cost_center": self.cost_center, + "posting_date": getdate(), + "company": self.company, + }, + item=self, + ) + ) + + def get_gl_entries_for_consumed_items(self, gl_entries, fixed_asset_account): + if not (self.get("stock_consumption") and self.get("stock_items")): + return + + # creating GL Entries for each row in Stock Items based on the Stock Entry created for it + stock_entry = frappe.get_doc("Stock Entry", self.stock_entry) + + default_expense_account = None + if not erpnext.is_perpetual_inventory_enabled(self.company): + default_expense_account = frappe.get_cached_value( + "Company", self.company, "default_expense_account" + ) + if not default_expense_account: + frappe.throw(_("Please set default Expense Account in Company {0}").format(self.company)) + + for item in stock_entry.items: + if flt(item.amount) > 0: + gl_entries.append( + self.get_gl_dict( + { + "account": item.expense_account or default_expense_account, + "credit": item.amount, + "credit_in_account_currency": item.amount, + "against": fixed_asset_account, + "voucher_type": self.doctype, + "voucher_no": self.name, + "cost_center": self.cost_center, + "posting_date": getdate(), + "company": self.company, + }, + item=self, + ) + ) + + gl_entries.append( + self.get_gl_dict( + { + "account": fixed_asset_account, + "debit": item.amount, + "debit_in_account_currency": item.amount, + "against": item.expense_account, + "voucher_type": self.doctype, + "voucher_no": self.name, + "cost_center": self.cost_center, + "posting_date": getdate(), + "against_voucher_type": "Stock Entry", + "against_voucher": self.stock_entry, + "company": self.company, + }, + item=self, + ) + ) def modify_depreciation_schedule(self): for row in self.asset_doc.finance_books: diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index 4e7cf78090..6e06f52ac6 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -6,6 +6,7 @@ import unittest import frappe from frappe.utils import flt, nowdate +from erpnext.assets.doctype.asset.asset import get_asset_account from erpnext.assets.doctype.asset.test_asset import ( create_asset, create_asset_data, @@ -125,10 +126,109 @@ class TestAssetRepair(unittest.TestCase): asset_repair = create_asset_repair(capitalize_repair_cost=1, submit=1) self.assertTrue(asset_repair.purchase_invoice) - def test_gl_entries(self): - asset_repair = create_asset_repair(capitalize_repair_cost=1, submit=1) - gl_entry = frappe.get_last_doc("GL Entry") - self.assertEqual(asset_repair.name, gl_entry.voucher_no) + def test_gl_entries_with_perpetual_inventory(self): + set_depreciation_settings_in_company(company="_Test Company with perpetual inventory") + + asset_category = frappe.get_doc("Asset Category", "Computers") + asset_category.append( + "accounts", + { + "company_name": "_Test Company with perpetual inventory", + "fixed_asset_account": "_Test Fixed Asset - TCP1", + "accumulated_depreciation_account": "_Test Accumulated Depreciations - TCP1", + "depreciation_expense_account": "_Test Depreciations - TCP1", + }, + ) + asset_category.save() + + asset_repair = create_asset_repair( + capitalize_repair_cost=1, + stock_consumption=1, + warehouse="Stores - TCP1", + company="_Test Company with perpetual inventory", + submit=1, + ) + + gl_entries = frappe.db.sql( + """ + select + account, + sum(debit) as debit, + sum(credit) as credit + from `tabGL Entry` + where + voucher_type='Asset Repair' + and voucher_no=%s + group by + account + """, + asset_repair.name, + as_dict=1, + ) + + self.assertTrue(gl_entries) + + fixed_asset_account = get_asset_account( + "fixed_asset_account", asset=asset_repair.asset, company=asset_repair.company + ) + pi_expense_account = ( + frappe.get_doc("Purchase Invoice", asset_repair.purchase_invoice).items[0].expense_account + ) + stock_entry_expense_account = ( + frappe.get_doc("Stock Entry", asset_repair.stock_entry).get("items")[0].expense_account + ) + + expected_values = { + fixed_asset_account: [asset_repair.total_repair_cost, 0], + pi_expense_account: [0, asset_repair.repair_cost], + stock_entry_expense_account: [0, 100], + } + + for d in gl_entries: + self.assertEqual(expected_values[d.account][0], d.debit) + self.assertEqual(expected_values[d.account][1], d.credit) + + def test_gl_entries_with_periodical_inventory(self): + frappe.db.set_value( + "Company", "_Test Company", "default_expense_account", "Cost of Goods Sold - _TC" + ) + asset_repair = create_asset_repair( + capitalize_repair_cost=1, + stock_consumption=1, + submit=1, + ) + + gl_entries = frappe.db.sql( + """ + select + account, + sum(debit) as debit, + sum(credit) as credit + from `tabGL Entry` + where + voucher_type='Asset Repair' + and voucher_no=%s + group by + account + """, + asset_repair.name, + as_dict=1, + ) + + self.assertTrue(gl_entries) + + fixed_asset_account = get_asset_account( + "fixed_asset_account", asset=asset_repair.asset, company=asset_repair.company + ) + default_expense_account = frappe.get_cached_value( + "Company", asset_repair.company, "default_expense_account" + ) + + expected_values = {fixed_asset_account: [1100, 0], default_expense_account: [0, 1100]} + + for d in gl_entries: + self.assertEqual(expected_values[d.account][0], d.debit) + self.assertEqual(expected_values[d.account][1], d.credit) def test_increase_in_asset_life(self): asset = create_asset(calculate_depreciation=1, submit=1) @@ -160,7 +260,7 @@ def create_asset_repair(**args): if args.asset: asset = args.asset else: - asset = create_asset(is_existing_asset=1, submit=1) + asset = create_asset(is_existing_asset=1, submit=1, company=args.company) asset_repair = frappe.new_doc("Asset Repair") asset_repair.update( { @@ -192,7 +292,7 @@ def create_asset_repair(**args): if args.submit: asset_repair.repair_status = "Completed" - asset_repair.cost_center = "_Test Cost Center - _TC" + asset_repair.cost_center = frappe.db.get_value("Company", asset.company, "cost_center") if args.stock_consumption: stock_entry = frappe.get_doc( @@ -204,6 +304,8 @@ def create_asset_repair(**args): "t_warehouse": asset_repair.warehouse, "item_code": asset_repair.stock_items[0].item_code, "qty": asset_repair.stock_items[0].consumed_quantity, + "basic_rate": args.rate if args.get("rate") is not None else 100, + "cost_center": asset_repair.cost_center, }, ) stock_entry.submit() @@ -213,7 +315,13 @@ def create_asset_repair(**args): asset_repair.repair_cost = 1000 if asset.calculate_depreciation: asset_repair.increase_in_asset_life = 12 - asset_repair.purchase_invoice = make_purchase_invoice().name + pi = make_purchase_invoice( + company=asset.company, + expense_account=frappe.db.get_value("Company", asset.company, "default_expense_account"), + cost_center=asset_repair.cost_center, + warehouse=asset_repair.warehouse, + ) + asset_repair.purchase_invoice = pi.name asset_repair.submit() return asset_repair diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index f34ec56dc0..f087d996ff 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -85,7 +85,6 @@ "depreciation_expense_account", "series_for_depreciation_entry", "expenses_included_in_asset_valuation", - "repair_and_maintenance_account", "column_break_40", "disposal_account", "depreciation_cost_center", @@ -234,7 +233,6 @@ "label": "Default Warehouse for Sales Return", "options": "Warehouse" }, - { "fieldname": "country", "fieldtype": "Link", @@ -678,12 +676,6 @@ "fieldtype": "Section Break", "label": "Fixed Asset Defaults" }, - { - "fieldname": "repair_and_maintenance_account", - "fieldtype": "Link", - "label": "Repair and Maintenance Account", - "options": "Account" - }, { "fieldname": "section_break_28", "fieldtype": "Section Break", @@ -709,7 +701,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2022-06-30 18:03:18.701314", + "modified": "2022-08-16 16:09:02.327724", "modified_by": "Administrator", "module": "Setup", "name": "Company", From 0e26df331c2358a9683f2e59b9b0ed3bfccad61e Mon Sep 17 00:00:00 2001 From: Solufyin <34390782+Solufyin@users.noreply.github.com> Date: Wed, 24 Aug 2022 13:28:55 +0530 Subject: [PATCH 0089/1047] fix: Route condition set for stock ledger (#31935) --- erpnext/stock/doctype/item/item_dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item_dashboard.py b/erpnext/stock/doctype/item/item_dashboard.py index 897acb7448..34bb4d1225 100644 --- a/erpnext/stock/doctype/item/item_dashboard.py +++ b/erpnext/stock/doctype/item/item_dashboard.py @@ -5,7 +5,7 @@ def get_data(): return { "heatmap": True, "heatmap_message": _("This is based on stock movement. See {0} for details").format( - '' + _("Stock Ledger") + "" + '' + _("Stock Ledger") + "" ), "fieldname": "item_code", "non_standard_fieldnames": { From 36f5883ddaf2b2215123d0b06f7b3965b27696c5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 24 Aug 2022 13:58:11 +0530 Subject: [PATCH 0090/1047] test: payments against so/po will show up as outstanding amount 1. Class will use FrappeTestCase fixture 2. setup and teardown methods are introduced 3. test for payments against SO --- .../test_accounts_receivable.py | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index edddbbce21..bac8beed2e 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -1,19 +1,27 @@ import unittest import frappe +from frappe.tests.utils import FrappeTestCase from frappe.utils import add_days, getdate, today from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order -class TestAccountsReceivable(unittest.TestCase): - def test_accounts_receivable(self): +class TestAccountsReceivable(FrappeTestCase): + def setUp(self): frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company 2'") + frappe.db.sql("delete from `tabSales Order` where company='_Test Company 2'") + frappe.db.sql("delete from `tabPayment Entry` where company='_Test Company 2'") frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'") frappe.db.sql("delete from `tabPayment Ledger Entry` where company='_Test Company 2'") + def tearDown(self): + frappe.db.rollback() + + def test_accounts_receivable(self): filters = { "company": "_Test Company 2", "based_on_payment_terms": 1, @@ -66,6 +74,50 @@ class TestAccountsReceivable(unittest.TestCase): ], ) + def test_payment_againt_po_in_receivable_report(self): + """ + Payments made against Purchase Order will show up as outstanding amount + """ + + so = make_sales_order( + company="_Test Company 2", + customer="_Test Customer 2", + warehouse="Finished Goods - _TC2", + currency="EUR", + debit_to="Debtors - _TC2", + income_account="Sales - _TC2", + expense_account="Cost of Goods Sold - _TC2", + cost_center="Main - _TC2", + ) + + pe = get_payment_entry(so.doctype, so.name) + pe = pe.save().submit() + + filters = { + "company": "_Test Company 2", + "based_on_payment_terms": 0, + "report_date": today(), + "range1": 30, + "range2": 60, + "range3": 90, + "range4": 120, + } + + report = execute(filters) + + expected_data_after_payment = [0, 1000, 0, -1000] + + row = report[1][0] + self.assertEqual( + expected_data_after_payment, + [ + row.invoiced, + row.paid, + row.credit_note, + row.outstanding, + ], + ) + def make_sales_invoice(): frappe.set_user("Administrator") From f9a7b31b5b39da7f3e12ad8274330dafda6ce2a8 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 24 Aug 2022 17:16:01 +0530 Subject: [PATCH 0091/1047] fix: Purposes not set --- .../doctype/maintenance_schedule/maintenance_schedule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index 09d4429712..3dc6b0f900 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -415,7 +415,7 @@ def make_maintenance_visit(source_name, target_doc=None, item_name=None, s_id=No }, "Maintenance Schedule Item": { "doctype": "Maintenance Visit Purpose", - "condition": lambda doc: doc.item_name == item_name, + "condition": lambda doc: doc.item_name == item_name if item_name else True, "field_map": {"sales_person": "service_person"}, "postprocess": update_serial, }, From 122f1c0cedf583451e85eb68093ede6f0ede43e4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Wed, 24 Aug 2022 18:24:39 +0530 Subject: [PATCH 0092/1047] fix: Explicitly commit "log_error" since its getting called during GET request (#31952) --- erpnext/erpnext_integrations/exotel_integration.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/erpnext_integrations/exotel_integration.py b/erpnext/erpnext_integrations/exotel_integration.py index fd9f74e8c9..fd0f783575 100644 --- a/erpnext/erpnext_integrations/exotel_integration.py +++ b/erpnext/erpnext_integrations/exotel_integration.py @@ -26,6 +26,7 @@ def handle_incoming_call(**kwargs): except Exception as e: frappe.db.rollback() exotel_settings.log_error("Error in Exotel incoming call") + frappe.db.commit() @frappe.whitelist(allow_guest=True) From 264f98af140cb8b73c417ef18bca16b838926927 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Wed, 24 Aug 2022 15:52:00 +0200 Subject: [PATCH 0093/1047] chore: update french translation --- erpnext/translations/fr.csv | 104 +++++++++++++++++------------------- 1 file changed, 49 insertions(+), 55 deletions(-) diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv index dbc319476b..1869411a1f 100644 --- a/erpnext/translations/fr.csv +++ b/erpnext/translations/fr.csv @@ -329,11 +329,11 @@ Average Rate,Prix moyen, Avg Daily Outgoing,Moy Quotidienne Sortante, Avg. Buying Price List Rate,Moyenne de la liste de prix d'achat, Avg. Selling Price List Rate,Prix moyen de la liste de prix de vente, -Avg. Selling Rate,Moy. Taux de vente, +Avg. Selling Rate,Moy. prix de vente, BOM,Nomenclature, BOM Browser,Explorateur Nomenclature, BOM No,N° Nomenclature, -BOM Rate,Valeur nomenclature, +BOM Rate,Cout nomenclature, BOM Stock Report,Rapport de Stock des nomenclatures, BOM and Manufacturing Quantity are required,Nomenclature et quantité de production sont nécessaires, BOM does not contain any stock item,Nomenclature ne contient aucun article en stock, @@ -561,9 +561,9 @@ Colour,Couleur, Combined invoice portion must equal 100%,La portion combinée de la facture doit être égale à 100%, Commercial,Commercial, Commission,Commission, -Commission Rate %,Taux de commission%, +Commission Rate %,Pourcentage de commission, Commission on Sales,Commission sur les ventes, -Commission rate cannot be greater than 100,Taux de commission ne peut pas être supérieure à 100, +Commission rate cannot be greater than 100,Pourcentage de commission ne peut pas être supérieure à 100, Community Forum,Forum de la communauté, Company (not Customer or Supplier) master.,Données de base de la Société (ni les Clients ni les Fournisseurs), Company Abbreviation,Abréviation de la Société, @@ -1072,7 +1072,7 @@ For Warehouse is required before Submit,Pour l’Entrepôt est requis avant de V "For an item {0}, quantity must be negative number","Pour l'article {0}, la quantité doit être un nombre négatif", "For an item {0}, quantity must be positive number","Pour un article {0}, la quantité doit être un nombre positif", "For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry","Pour la carte de travail {0}, vous pouvez uniquement saisir une entrée de stock de type "Transfert d'article pour fabrication".", -"For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included","Pour la ligne {0} dans {1}. Pour inclure {2} dans le prix de l'Article, les lignes {3} doivent également être incluses", +"For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included","Pour la ligne {0} dans {1}. Pour inclure {2} dans le prix de l'article, les lignes {3} doivent également être incluses", For row {0}: Enter Planned Qty,Pour la ligne {0}: entrez la quantité planifiée, "For {0}, only credit accounts can be linked against another debit entry","Pour {0}, seuls les comptes de crédit peuvent être liés avec une autre écriture de débit", "For {0}, only debit accounts can be linked against another credit entry","Pour {0}, seuls les comptes de débit peuvent être liés avec une autre écriture de crédit", @@ -1235,7 +1235,7 @@ ITC Reversed,CTI inversé, Identifying Decision Makers,Identifier les décideurs, "If Auto Opt In is checked, then the customers will be automatically linked with the concerned Loyalty Program (on save)","Si l'option adhésion automatique est cochée, les clients seront automatiquement liés au programme de fidélité concerné (après l'enregistrement)", "If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.","Si plusieurs Règles de Prix continuent de prévaloir, les utilisateurs sont invités à définir manuellement la priorité pour résoudre les conflits.", -"If selected Pricing Rule is made for 'Rate', it will overwrite Price List. Pricing Rule rate is the final rate, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field.","Si la règle de tarification sélectionnée est définie pour le «Prix Unitaire», elle écrase la liste de prix. Le prix unitaire de la règle de tarification est le prix unitaire final, donc aucune autre réduction supplémentaire ne doit être appliquée. Par conséquent, dans les transactions telles que la commande client, la commande d'achat, etc., elle sera récupérée dans le champ ""Prix Unitaire"", plutôt que dans le champ ""Tarif de la liste de prix"".", +"If selected Pricing Rule is made for 'Rate', it will overwrite Price List. Pricing Rule rate is the final rate, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field.","Si la règle de tarification sélectionnée est définie pour le 'Prix Unitaire', elle écrase la liste de prix. Le prix unitaire de la règle de tarification est le prix unitaire final, donc aucune autre réduction supplémentaire ne doit être appliquée. Par conséquent, dans les transactions telles que la commande client, la commande d'achat, etc., elle sera récupérée dans le champ 'Prix Unitaire', plutôt que dans le champ 'Tarif de la liste de prix'.", "If two or more Pricing Rules are found based on the above conditions, Priority is applied. Priority is a number between 0 to 20 while default value is zero (blank). Higher number means it will take precedence if there are multiple Pricing Rules with same conditions.","Si deux Règles de Prix ou plus sont trouvées sur la base des conditions ci-dessus, une Priorité est appliquée. La Priorité est un nombre compris entre 0 et 20 avec une valeur par défaut de zéro (vide). Les nombres les plus élévés sont prioritaires s'il y a plusieurs Règles de Prix avec mêmes conditions.", "If unlimited expiry for the Loyalty Points, keep the Expiry Duration empty or 0.","Si vous souhaitez ne pas mettre de date d'expiration pour les points de fidélité, laissez la durée d'expiration vide ou mettez 0.", "If you have any questions, please get back to us.","Si vous avez des questions, veuillez revenir vers nous.", @@ -1269,7 +1269,7 @@ Income,Revenus, Income Account,Compte de Produits, Income Tax,Impôt sur le revenu, Incoming,Entrant, -Incoming Rate,Taux d'Entrée, +Incoming Rate,Prix d'Entrée, Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction.,Nombre incorrect d'Écritures Grand Livre trouvées. Vous avez peut-être choisi le mauvais Compte dans la transaction., Increment cannot be 0,Incrément ne peut pas être 0, Increment for Attribute {0} cannot be 0,Incrément pour l'Attribut {0} ne peut pas être 0, @@ -1365,7 +1365,7 @@ Item Variants,Variantes de l'Article, Item Variants updated,Variantes d'article mises à jour, Item has variants.,L'article a des variantes., Item must be added using 'Get Items from Purchase Receipts' button,L'article doit être ajouté à l'aide du bouton 'Obtenir des éléments de Reçus d'Achat', -Item valuation rate is recalculated considering landed cost voucher amount,Le taux d'évaluation de l'article est recalculé compte tenu du montant du bon de prix au débarquement, +Item valuation rate is recalculated considering landed cost voucher amount,Le taux de valorisation de l'article est recalculé compte tenu du montant du bon de prix au débarquement, Item variant {0} exists with same attributes,La variante de l'article {0} existe avec les mêmes caractéristiques, Item {0} does not exist,Article {0} n'existe pas, Item {0} does not exist in the system or has expired,L'article {0} n'existe pas dans le système ou a expiré, @@ -2147,7 +2147,7 @@ Previous Financial Year is not closed,L’Exercice Financier Précédent n’est Price,Prix, Price List,Liste de prix, Price List Currency not selected,Devise de la Liste de Prix non sélectionnée, -Price List Rate,Taux de la Liste des Prix, +Price List Rate,Prix de la Liste des Prix, Price List master.,Données de Base des Listes de Prix, Price List must be applicable for Buying or Selling,La Liste de Prix doit être applicable pour les Achats et les Ventes, Price List {0} is disabled or does not exist,Liste des Prix {0} est désactivée ou n'existe pas, @@ -2288,8 +2288,8 @@ Quotations: ,Devis :, Quotes to Leads or Customers.,Devis de Prospects ou Clients., RFQs are not allowed for {0} due to a scorecard standing of {1},Les Appels d'Offres ne sont pas autorisés pour {0} en raison d'une note de {1} sur la fiche d'évaluation, Range,Plage, -Rate,Taux, -Rate:,Taux:, +Rate,Prix, +Rate:,Prix:, Rating,Évaluation, Raw Material,Matières Premières, Raw Materials,Matières premières, @@ -2426,7 +2426,7 @@ Route,Route, Row # {0}: ,Ligne # {0} :, Row # {0}: Batch No must be same as {1} {2},Ligne # {0} : Le N° de Lot doit être le même que {1} {2}, Row # {0}: Cannot return more than {1} for Item {2},Ligne # {0} : Vous ne pouvez pas retourner plus de {1} pour l’Article {2}, -Row # {0}: Rate cannot be greater than the rate used in {1} {2},Ligne # {0}: Le Taux ne peut pas être supérieur au taux utilisé dans {1} {2}, +Row # {0}: Rate cannot be greater than the rate used in {1} {2},Ligne # {0}: Le prix ne peut pas être supérieur au prix utilisé dans {1} {2}, Row # {0}: Serial No is mandatory,Ligne # {0} : N° de série est obligatoire, Row # {0}: Serial No {1} does not match with {2} {3},Ligne # {0} : N° de série {1} ne correspond pas à {2} {3}, Row #{0} (Payment Table): Amount must be negative,Row # {0} (Table de paiement): le montant doit être négatif, @@ -2434,7 +2434,7 @@ Row #{0} (Payment Table): Amount must be positive,Ligne #{0} (Table de paiement) Row #{0}: Account {1} does not belong to company {2},Ligne # {0}: le compte {1} n'appartient pas à la société {2}, Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Ligne # {0}: montant attribué ne peut pas être supérieur au montant en souffrance., "Row #{0}: Asset {1} cannot be submitted, it is already {2}","Ligne #{0} : L’Actif {1} ne peut pas être soumis, il est déjà {2}", -Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,Ligne n ° {0}: impossible de définir le tarif si le montant est supérieur au montant facturé pour l'élément {1}., +Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,Ligne n ° {0}: impossible de définir le prix si le montant est supérieur au montant facturé pour l'élément {1}., Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Ligne #{0} : Date de compensation {1} ne peut pas être antérieure à la Date du Chèque {2}, Row #{0}: Duplicate entry in References {1} {2},Ligne # {0}: entrée en double dans les références {1} {2}, Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Ligne {0}: la date de livraison prévue ne peut pas être avant la date de commande, @@ -2898,7 +2898,7 @@ Sync has been temporarily disabled because maximum retries have been exceeded,La Syntax error in condition: {0},Erreur de syntaxe dans la condition: {0}, Syntax error in formula or condition: {0},Erreur de syntaxe dans la formule ou condition : {0}, System Manager,Responsable Système, -TDS Rate %,Taux de TDS%, +TDS Rate %,Pourcentage de TDS, Tap items to add them here,Choisissez des articles pour les ajouter ici, Target,Cible, Target ({}),Cible ({}), @@ -3190,7 +3190,7 @@ Update Print Format,Mettre à Jour le Format d'Impression, Update Response,Mettre à jour la réponse, Update bank payment dates with journals.,Mettre à jour les dates de paiement bancaires avec les journaux., Update in progress. It might take a while.,Mise à jour en cours. Ça peut prendre un moment., -Update rate as per last purchase,Taux de mise à jour selon le dernier achat, +Update rate as per last purchase,Mettre à jour les prix selon le dernier prix achat, Update stock must be enable for the purchase invoice {0},La mise à jour du stock doit être activée pour la facture d'achat {0}, Updating Variants...,Mise à jour des variantes ..., Upload your letter head and logo. (you can edit them later).,Charger votre en-tête et logo. (vous pouvez les modifier ultérieurement)., @@ -3326,7 +3326,6 @@ You are not authorized to add or update entries before {0},Vous n'êtes pas auto You are not authorized to approve leaves on Block Dates,Vous n'êtes pas autorisé à approuver les congés sur les Dates Bloquées, You are not authorized to set Frozen value,Vous n'êtes pas autorisé à définir des valeurs gelées, You are not present all day(s) between compensatory leave request days,Vous n'êtes pas présent(e) tous les jours vos demandes de congé compensatoire, -You can not change rate if BOM mentioned agianst any item,Vous ne pouvez pas modifier le taux si la nomenclature est mentionnée pour un article, You can not enter current voucher in 'Against Journal Entry' column,Vous ne pouvez pas entrer le bon actuel dans la colonne 'Pour l'Écriture de Journal', You can only have Plans with the same billing cycle in a Subscription,Vous ne pouvez avoir que des plans ayant le même cycle de facturation dans le même abonnement, You can only redeem max {0} points in this order.,Vous pouvez uniquement échanger un maximum de {0} points dans cet commande., @@ -3971,7 +3970,7 @@ Queued,File d'Attente, Quick Entry,Écriture Rapide, Quiz {0} does not exist,Le questionnaire {0} n'existe pas, Quotation Amount,Montant du devis, -Rate or Discount is required for the price discount.,Le taux ou la remise est requis pour la remise de prix., +Rate or Discount is required for the price discount.,Le prix ou la remise est requis pour la remise., Reason,Raison, Reconcile Entries,Réconcilier les entrées, Reconcile this account,Réconcilier ce compte, @@ -4348,7 +4347,7 @@ Valid Upto date cannot be before Valid From date,La date de validité valide ne Valid From date not in Fiscal Year {0},Date de début de validité non comprise dans l'exercice {0}, Valid Upto date not in Fiscal Year {0},Valable jusqu'à la date hors exercice {0}, Group Roll No,Groupe Roll Non, -Maintain Same Rate Throughout Sales Cycle,Maintenir le Même Taux Durant le Cycle de Vente, +Maintain Same Rate Throughout Sales Cycle,Maintenir le même prix Durant le Cycle de Vente, "Row {1}: Quantity ({0}) cannot be a fraction. To allow this, disable '{2}' in UOM {3}.","Ligne {1}: la quantité ({0}) ne peut pas être une fraction. Pour autoriser cela, désactivez «{2}» dans UdM {3}.", Must be Whole Number,Doit être un Nombre Entier, Please setup Razorpay Plan ID,Veuillez configurer l'ID du plan Razorpay, @@ -4961,7 +4960,7 @@ Threshold for Suggestion,Seuil de suggestion, System will notify to increase or decrease quantity or amount ,Le système notifiera d'augmenter ou de diminuer la quantité ou le montant, "Higher the number, higher the priority","Plus le nombre est grand, plus la priorité est haute", Apply Multiple Pricing Rules,Appliquer plusieurs règles de tarification, -Apply Discount on Rate,Appliquer une réduction sur le taux, +Apply Discount on Rate,Appliquer une réduction sur le prix, Validate Applied Rule,Valider la règle appliquée, Rule Description,Description de la règle, Pricing Rule Help,Aide pour les Règles de Tarification, @@ -5050,19 +5049,18 @@ End date of current invoice's period,Date de fin de la période de facturation e Update Auto Repeat Reference,Mettre à jour la référence de répétition automatique, Purchase Invoice Advance,Avance sur Facture d’Achat, Purchase Invoice Item,Article de la Facture d'Achat, -Quantity and Rate,Quantité et Taux, +Quantity and Rate,Quantité et Prix, Received Qty,Qté Reçue, Accepted Qty,Quantité acceptée, Rejected Qty,Qté Rejetée, UOM Conversion Factor,Facteur de Conversion de l'UDM, Discount on Price List Rate (%),Remise sur la Liste des Prix (%), Price List Rate (Company Currency),Taux de la Liste de Prix (Devise Société), -Rate ,Taux, Rate (Company Currency),Prix (Devise Société), Amount (Company Currency),Montant (Devise de la Société), Is Free Item,Est un article gratuit, -Net Rate,Taux Net, -Net Rate (Company Currency),Taux Net (Devise Société), +Net Rate,Prix Net, +Net Rate (Company Currency),Prix Net (Devise Société), Net Amount (Company Currency),Montant Net (Devise Société), Item Tax Amount Included in Value,Montant de la taxe incluse dans la valeur, Landed Cost Voucher Amount,Montant de la Référence de Coût au Débarquement, @@ -5080,7 +5078,7 @@ Enable Deferred Expense,Activer les frais reportés, Service Start Date,Date de début du service, Service End Date,Date de fin du service, Allow Zero Valuation Rate,Autoriser un Taux de Valorisation Égal à Zéro, -Item Tax Rate,Taux de la Taxe sur l'Article, +Item Tax Rate,Prix de la Taxe sur l'Article, Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges,La table de détails de taxe est récupérée depuis les données de base de l'article comme une chaîne de caractères et stockée dans ce champ. Elle est utilisée pour les Taxes et Frais., Purchase Order Item,Article de la Commande d'Achat, Purchase Receipt Detail,Détail du reçu d'achat, @@ -5098,8 +5096,8 @@ On Previous Row Amount,Le Montant de la Rangée Précédente, On Previous Row Total,Le Total de la Rangée Précédente, On Item Quantity,Sur quantité d'article, Reference Row #,Ligne de Référence #, -Is this Tax included in Basic Rate?,Cette Taxe est-elle incluse dans le Taux de Base ?, -"If checked, the tax amount will be considered as already included in the Print Rate / Print Amount","Si cochée, le montant de la taxe sera considéré comme déjà inclus dans le Taux d'Impression / Prix d'Impression", +Is this Tax included in Basic Rate?,Cette Taxe est-elle incluse dans le Prix de Base ?, +"If checked, the tax amount will be considered as already included in the Print Rate / Print Amount","Si cochée, le montant de la taxe sera considéré comme déjà inclus dans le Taux / Prix des documents (PDF, impressions)", Account Head,Compte Principal, Tax Amount After Discount Amount,Montant de la Taxe après Remise, Item Wise Tax Detail ,Détail de la taxe de l'article Wise, @@ -5147,7 +5145,7 @@ Accounting Details,Détails Comptabilité, Debit To,Débit Pour, Is Opening Entry,Est Écriture Ouverte, C-Form Applicable,Formulaire-C Applicable, -Commission Rate (%),Taux de Commission (%), +Commission Rate (%),Pourcentage de Commission, Sales Team1,Équipe des Ventes 1, Against Income Account,Pour le Compte de Produits, Sales Invoice Advance,Avance sur Facture de Vente, @@ -5157,9 +5155,9 @@ Customer's Item Code,Code de l'Article du Client, Brand Name,Nom de la Marque, Qty as per Stock UOM,Qté par UDM du Stock, Discount and Margin,Remise et Marge, -Rate With Margin,Tarif Avec Marge, -Discount (%) on Price List Rate with Margin,Remise (%) sur le Tarif de la Liste de Prix avec la Marge, -Rate With Margin (Company Currency),Taux avec marge (devise de l'entreprise), +Rate With Margin,Prix Avec Marge, +Discount (%) on Price List Rate with Margin,Remise (%) sur le prix de la Liste de Prix avec la Marge, +Rate With Margin (Company Currency),Prix avec marge (devise de l'entreprise), Delivered By Supplier,Livré par le Fournisseur, Deferred Revenue,Produits comptabilisés d'avance, Deferred Revenue Account,Compte de produits comptabilisés d'avance, @@ -5721,7 +5719,7 @@ Contact Mobile No,N° de Portable du Contact, Enter name of campaign if source of enquiry is campaign,Entrez le nom de la campagne si la source de l'enquête est une campagne, Opportunity Date,Date d'Opportunité, Opportunity Item,Article de l'Opportunité, -Basic Rate,Taux de Base, +Basic Rate,Prix de Base, Stage Name,Nom de scène, Social Media Post,Publication sur les réseaux sociaux, Post Status,Statut du message, @@ -7218,8 +7216,8 @@ Qty Consumed Per Unit,Qté Consommée Par Unité, Include Item In Manufacturing,Inclure l'article dans la fabrication, BOM Item,Article de la nomenclature, Item operation,Opération de l'article, -Rate & Amount,Taux et Montant, -Basic Rate (Company Currency),Taux de Base (Devise de la Société ), +Rate & Amount,Prix et Montant, +Basic Rate (Company Currency),Prix de Base (Devise de la Société ), Scrap %,% de Rebut, Original Item,Article original, BOM Operation,Opération de la nomenclature (gamme), @@ -7464,8 +7462,8 @@ Website Attribute,Attribut de site Web, Attribute,Attribut, Website Filter Field,Champ de filtrage de site Web, Activity Cost,Coût de l'Activité, -Billing Rate,Taux de Facturation, -Costing Rate,Taux des Coûts, +Billing Rate,Prix de Facturation, +Costing Rate,Tarifs des Coûts, title,Titre, Projects User,Utilisateur/Intervenant Projets, Default Costing Rate,Coût de Revient par Défaut, @@ -7963,7 +7961,7 @@ Reserved Quantity,Quantité Réservée, Actual Quantity,Quantité Réelle, Requested Quantity,Quantité Demandée, Reserved Qty for sub contract,Qté réservée pour le sous-contrat, -Moving Average Rate,Taux Mobile Moyen, +Moving Average Rate,Prix moyen pondéré, FCFS Rate,Montant PAPS, Customs Tariff Number,Tarifs Personnalisés, Tariff Number,Tarif, @@ -8311,7 +8309,7 @@ Total Additional Costs,Total des Coûts Additionnels, Customer or Supplier Details,Détails du Client ou du Fournisseur, Per Transferred,Par transféré, Stock Entry Detail,Détails de l'Écriture de Stock, -Basic Rate (as per Stock UOM),Taux de base (comme l’UDM du Stock), +Basic Rate (as per Stock UOM),Prix de base (comme l’UDM du Stock), Basic Amount,Montant de Base, Additional Cost,Frais Supplémentaire, Serial No / Batch,N° de Série / Lot, @@ -8323,7 +8321,7 @@ Stock Entry Child,Entrée de stock enfant, PO Supplied Item,PO article fourni, Reference Purchase Receipt,Reçu d'achat de référence, Stock Ledger Entry,Écriture du Livre d'Inventaire, -Outgoing Rate,Taux Sortant, +Outgoing Rate,Prix Sortant, Actual Qty After Transaction,Qté Réelle Après Transaction, Stock Value Difference,Différence de Valeur du Sock, Stock Queue (FIFO),File d'Attente du Stock (FIFO), @@ -8509,7 +8507,7 @@ Item Price Stock,Stock et prix de l'article, Item Prices,Prix des Articles, Item Shortage Report,Rapport de Rupture de Stock d'Article, Item Variant Details,Détails de la variante de l'article, -Item-wise Price List Rate,Taux de la Liste des Prix par Article, +Item-wise Price List Rate,Prix de la Liste des Prix par Article, Item-wise Purchase History,Historique d'Achats par Article, Item-wise Purchase Register,Registre des Achats par Article, Item-wise Sales History,Historique des Ventes par Article, @@ -8561,7 +8559,7 @@ Sales Partner Target Variance based on Item Group,Variance cible du partenaire c Sales Partner Transaction Summary,Récapitulatif des transactions du partenaire commercial, Sales Partners Commission,Commission des Partenaires de Vente, Invoiced Amount (Exclusive Tax),Montant facturé (taxe exclusive), -Average Commission Rate,Taux Moyen de la Commission, +Average Commission Rate,Coût Moyen de la Commission, Sales Payment Summary,Résumé du paiement des ventes, Sales Person Commission Summary,Récapitulatif de la commission des ventes, Sales Person Target Variance Based On Item Group,Écart cible du commercial basé sur le groupe de postes, @@ -8815,7 +8813,7 @@ Generate New Invoices Past Due Date,Générer de nouvelles factures en retard, New invoices will be generated as per schedule even if current invoices are unpaid or past due date,"De nouvelles factures seront générées selon le calendrier, même si les factures actuelles sont impayées ou en retard", Document Type ,Type de document, Subscription Price Based On,Prix d'abonnement basé sur, -Fixed Rate,Taux fixe, +Fixed Rate,Tarif fixe, Based On Price List,Basé sur la liste de prix, Monthly Rate,Tarif mensuel, Cancel Subscription After Grace Period,Annuler l'abonnement après la période de grâce, @@ -8886,10 +8884,6 @@ Practitioner Name,Nom du praticien, Enter a name for the Clinical Procedure Template,Entrez un nom pour le modèle de procédure clinique, Set the Item Code which will be used for billing the Clinical Procedure.,Définissez le code article qui sera utilisé pour facturer la procédure clinique., Select an Item Group for the Clinical Procedure Item.,Sélectionnez un groupe d'articles pour l'article de procédure clinique., -Clinical Procedure Rate,Taux de procédure clinique, -Check this if the Clinical Procedure is billable and also set the rate.,Cochez cette case si la procédure clinique est facturable et définissez également le tarif., -Check this if the Clinical Procedure utilises consumables. Click ,Vérifiez ceci si la procédure clinique utilise des consommables. Cliquez sur, - to know more,en savoir plus, "You can also set the Medical Department for the template. After saving the document, an Item will automatically be created for billing this Clinical Procedure. You can then use this template while creating Clinical Procedures for Patients. Templates save you from filling up redundant data every single time. You can also create templates for other operations like Lab Tests, Therapy Sessions, etc.","Vous pouvez également définir le service médical du modèle. Après avoir enregistré le document, un élément sera automatiquement créé pour facturer cette procédure clinique. Vous pouvez ensuite utiliser ce modèle lors de la création de procédures cliniques pour les patients. Les modèles vous évitent de remplir des données redondantes à chaque fois. Vous pouvez également créer des modèles pour d'autres opérations telles que des tests de laboratoire, des séances de thérapie, etc.", Descriptive Test Result,Résultat du test descriptif, Allow Blank,Autoriser le blanc, @@ -9033,7 +9027,7 @@ Work In Progress Warehouse,Entrepôt de travaux en cours, This Warehouse will be auto-updated in the Work In Progress Warehouse field of Work Orders.,Cet entrepôt sera mis à jour automatiquement dans le champ Entrepôt de travaux en cours des bons de travail., Finished Goods Warehouse,Entrepôt de produits finis, This Warehouse will be auto-updated in the Target Warehouse field of Work Order.,Cet entrepôt sera mis à jour automatiquement dans le champ Entrepôt cible de l'ordre de fabrication. -"If ticked, the BOM cost will be automatically updated based on Valuation Rate / Price List Rate / last purchase rate of raw materials.","Si coché, le coût de la nomenclature sera automatiquement mis à jour en fonction du taux de valorisation / tarif tarifaire / dernier taux d'achat des matières premières.", +"If ticked, the BOM cost will be automatically updated based on Valuation Rate / Price List Rate / last purchase rate of raw materials.","Si coché, le coût de la nomenclature sera automatiquement mis à jour en fonction du taux de valorisation / prix de la liste prix / dernier prix d'achat des matières premières.", Source Warehouses (Optional),Entrepôts d'origine (facultatif), "System will pickup the materials from the selected warehouses. If not specified, system will create material request for purchase.","Le système ramassera les matériaux dans les entrepôts sélectionnés. S'il n'est pas spécifié, le système créera une demande de matériel pour l'achat.", Lead Time,Délai de mise en œuvre, @@ -9107,7 +9101,7 @@ MAT-PR-RET-.YYYY.-,MAT-PR-RET-.YYYY.-, Track this Purchase Receipt against any Project,Suivre ce reçu d'achat par rapport à n'importe quel projet, Please Select a Supplier,Veuillez sélectionner un fournisseur, Add to Transit,Ajouter à Transit, -Set Basic Rate Manually,Définir manuellement le taux de base, +Set Basic Rate Manually,Définir manuellement le prix de base, "By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a ","Par défaut, le nom de l'article est défini selon le code d'article entré. Si vous souhaitez que les éléments soient nommés par un", Set a Default Warehouse for Inventory Transactions. This will be fetched into the Default Warehouse in the Item master.,Définissez un entrepôt par défaut pour les mouvements de stock. Ce sera récupéré dans l'entrepôt par défaut dans la base d'articles., "This will allow stock items to be displayed in negative values. Using this option depends on your use case. With this option unchecked, the system warns before obstructing a transaction that is causing negative stock.","Cela permettra aux articles en stock d'être affichés avec des valeurs négatives. L'utilisation de cette option dépend de votre cas d'utilisation. Lorsque cette option n'est pas cochée, le système avertit avant d'entraver une transaction entraînant un stock négatif.", @@ -9495,7 +9489,7 @@ Normal Range: ,Plage normale:, Row #{0}: Check Out datetime cannot be less than Check In datetime,Ligne n ° {0}: la date de sortie ne peut pas être inférieure à la date de sortie, "Missing required details, did not create Inpatient Record","Détails requis manquants, n'a pas créé de dossier d'hospitalisation", Unbilled Invoices,Factures non facturées, -Standard Selling Rate should be greater than zero.,Le taux de vente standard doit être supérieur à zéro., +Standard Selling Rate should be greater than zero.,Le prix de vente standard doit être supérieur à zéro., Conversion Factor is mandatory,Le facteur de conversion est obligatoire, Row #{0}: Conversion Factor is mandatory,Ligne n ° {0}: le facteur de conversion est obligatoire, Sample Quantity cannot be negative or 0,La quantité d'échantillon ne peut pas être négative ou 0, @@ -9559,7 +9553,7 @@ The Request for Quotation can be accessed by clicking on the following button,La Regards,Cordialement, Please click on the following button to set your new password,Veuillez cliquer sur le bouton suivant pour définir votre nouveau mot de passe, Update Password,Mettre à jour le mot de passe, -Row #{}: Selling rate for item {} is lower than its {}. Selling {} should be atleast {},Ligne n ° {}: le taux de vente de l'article {} est inférieur à son {}. La vente {} doit être au moins {}, +Row #{}: Selling rate for item {} is lower than its {}. Selling {} should be atleast {},Ligne n ° {}: le prix de vente de l'article {} est inférieur à son {}. La vente {} doit être au moins {}, You can alternatively disable selling price validation in {} to bypass this validation.,Vous pouvez également désactiver la validation du prix de vente dans {} pour contourner cette validation., Invalid Selling Price,Prix de vente invalide, Address needs to be linked to a Company. Please add a row for Company in the Links table.,L'adresse doit être liée à une entreprise. Veuillez ajouter une ligne pour Entreprise dans le tableau Liens., @@ -9581,7 +9575,7 @@ Only select this if you have set up the Cash Flow Mapper documents,Sélectionnez Payment Channel,Canal de paiement, Is Purchase Order Required for Purchase Invoice & Receipt Creation?,Une Commande d'Achat est-il requis pour la création de factures d'achat et de reçus?, Is Purchase Receipt Required for Purchase Invoice Creation?,Un reçu d'achat est-il requis pour la création d'une facture d'achat?, -Maintain Same Rate Throughout the Purchase Cycle,Maintenir le même taux tout au long du cycle d'achat, +Maintain Same Rate Throughout the Purchase Cycle,Maintenir les même prix tout au long du cycle d'achat, Allow Item To Be Added Multiple Times in a Transaction,Autoriser l'ajout d'un article plusieurs fois dans une transaction, Suppliers,Fournisseurs, Send Emails to Suppliers,Envoyer des e-mails aux fournisseurs, @@ -9633,7 +9627,7 @@ Plan operations X days in advance,Planifier les opérations X jours à l'avance, Time Between Operations (Mins),Temps entre les opérations (minutes), Default: 10 mins,Par défaut: 10 minutes, Overproduction for Sales and Work Order,Surproduction pour les ventes et les bons de travail, -"Update BOM cost automatically via scheduler, based on the latest Valuation Rate/Price List Rate/Last Purchase Rate of raw materials","Mettre à jour automatiquement le coût de la nomenclature via le planificateur, en fonction du dernier taux de valorisation / tarif tarifaire / dernier taux d'achat de matières premières", +"Update BOM cost automatically via scheduler, based on the latest Valuation Rate/Price List Rate/Last Purchase Rate of raw materials","Mettre à jour automatiquement le coût de la nomenclature via le planificateur, en fonction du dernier taux de valorisation / prix de la liste de prix / dernier prix d'achat de matières premières", Purchase Order already created for all Sales Order items,Commande d'Achat déjà créé pour tous les articles de commande client, Select Items,Sélectionner des éléments, Against Default Supplier,Contre le fournisseur par défaut, @@ -9641,14 +9635,14 @@ Auto close Opportunity after the no. of days mentioned above,Opportunité de fer Is Sales Order Required for Sales Invoice & Delivery Note Creation?,Une commande client est-elle requise pour la création de factures clients et de bons de livraison?, Is Delivery Note Required for Sales Invoice Creation?,Un bon de livraison est-il nécessaire pour la création de factures de vente?, How often should Project and Company be updated based on Sales Transactions?,À quelle fréquence le projet et l'entreprise doivent-ils être mis à jour en fonction des transactions de vente?, -Allow User to Edit Price List Rate in Transactions,Autoriser l'utilisateur à modifier le tarif tarifaire dans les transactions, +Allow User to Edit Price List Rate in Transactions,Autoriser l'utilisateur à modifier le prix de la liste prix dans les transactions, Allow Item to Be Added Multiple Times in a Transaction,Autoriser l'ajout d'un article plusieurs fois dans une transaction, Allow Multiple Sales Orders Against a Customer's Purchase Order,Autoriser plusieurs commandes client par rapport à la commande d'achat d'un client, -Validate Selling Price for Item Against Purchase Rate or Valuation Rate,Valider le prix de vente de l'article par rapport au taux d'achat ou au taux de valorisation, +Validate Selling Price for Item Against Purchase Rate or Valuation Rate,Valider le prix de vente de l'article par rapport au prix d'achat ou au taux de valorisation, Hide Customer's Tax ID from Sales Transactions,Masquer le numéro d'identification fiscale du client dans les transactions de vente, "The percentage you are allowed to receive or deliver more against the quantity ordered. For example, if you have ordered 100 units, and your Allowance is 10%, then you are allowed to receive 110 units.","Le pourcentage que vous êtes autorisé à recevoir ou à livrer plus par rapport à la quantité commandée. Par exemple, si vous avez commandé 100 unités et que votre allocation est de 10%, vous êtes autorisé à recevoir 110 unités.", Action If Quality Inspection Is Not Submitted,Action si l'inspection qualité n'est pas soumise, -Auto Insert Price List Rate If Missing,Taux de liste de prix d'insertion automatique s'il est manquant, +Auto Insert Price List Rate If Missing,Insérer automatiquement le prix dand liste de prix s'il est manquant, Automatically Set Serial Nos Based on FIFO,Définir automatiquement les numéros de série en fonction de FIFO, Set Qty in Transactions Based on Serial No Input,Définir la quantité dans les transactions en fonction du numéro de série, Raise Material Request When Stock Reaches Re-order Level,Augmenter la demande d'article lorsque le stock atteint le niveau de commande, @@ -9722,7 +9716,7 @@ No Inpatient Record found against patient {0},Aucun dossier d'hospitalisation tr An Inpatient Medication Order {0} against Patient Encounter {1} already exists.,Une ordonnance de médicament pour patients hospitalisés {0} contre rencontre avec un patient {1} existe déjà., Allow In Returns,Autoriser les retours, Hide Unavailable Items,Masquer les éléments non disponibles, -Apply Discount on Discounted Rate,Appliquer une remise sur un tarif réduit, +Apply Discount on Discounted Rate,Appliquer une remise sur un prix réduit, Therapy Plan Template,Modèle de plan de thérapie, Fetching Template Details,Récupération des détails du modèle, Linked Item Details,Détails de l'élément lié, From 1f6f2747d49604bd3707d9017a219989d8df57b8 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Wed, 24 Aug 2022 21:20:23 +0200 Subject: [PATCH 0094/1047] chore: update fr translation --- erpnext/translations/fr.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv index 1869411a1f..b5de5784c5 100644 --- a/erpnext/translations/fr.csv +++ b/erpnext/translations/fr.csv @@ -658,7 +658,7 @@ Create Invoice,Créer une facture, Create Invoices,Créer des factures, Create Job Card,Créer une carte de travail, Create Journal Entry,Créer une entrée de journal, -Create Lead,Créer une piste, +Create Lead,Créer un Prospect, Create Leads,Créer des Prospects, Create Maintenance Visit,Créer une visite de maintenance, Create Material Request,Créer une demande de matériel, @@ -3881,7 +3881,7 @@ Only expired allocation can be cancelled,Seule l'allocation expirée peut être Only users with the {0} role can create backdated leave applications,Seuls les utilisateurs avec le rôle {0} peuvent créer des demandes de congé antidatées, Open,Ouvert, Open Contact,Contact ouvert, -Open Lead,Ouvrir le fil, +Open Lead,Ouvrir le Prospect, Opening and Closing,Ouverture et fermeture, Operating Cost as per Work Order / BOM,Coût d'exploitation selon l'ordre de fabrication / nomenclature, Order Amount,Montant de la commande, From 299da5d596f29d195c224af3021edd26f752d390 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Wed, 24 Aug 2022 21:29:22 +0200 Subject: [PATCH 0095/1047] chore: update fr translation --- erpnext/translations/fr.csv | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv index b5de5784c5..b2074618a6 100644 --- a/erpnext/translations/fr.csv +++ b/erpnext/translations/fr.csv @@ -9921,4 +9921,3 @@ Enable Reviews and Ratings,Activer les avis et notes Enable Recommendations,Activer les recommendations Item Search Settings,Paramétrage de la recherche d'article Purchase demande,Demande de materiel -Calendar,Calendier From e5b04d54ffbc6c2bed5df8280e39f9afb7c6555b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 24 Aug 2022 17:05:13 +0530 Subject: [PATCH 0096/1047] fix: display amount in account currency if party is supplied --- .../report/accounts_receivable/accounts_receivable.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index eb2959e197..63242e83fe 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -192,7 +192,11 @@ class ReceivablePayableReport(object): if not row: return - amount = ple.amount + # amount in "Party Currency", if its supplied. If not, amount in company currency + if self.filters.get(scrub(self.party_type)): + amount = ple.amount_in_account_currency + else: + amount = ple.amount amount_in_account_currency = ple.amount_in_account_currency # update voucher @@ -690,7 +694,7 @@ class ReceivablePayableReport(object): ple.party, ple.posting_date, ple.due_date, - ple.account_currency.as_("currency"), + ple.account_currency, ple.amount, ple.amount_in_account_currency, ) From 915102a40040e38239024acb2647f54fe350809b Mon Sep 17 00:00:00 2001 From: Samuel Danieli <23150094+scdanieli@users.noreply.github.com> Date: Thu, 25 Aug 2022 07:53:38 +0200 Subject: [PATCH 0097/1047] chore: german translations (#31463) --- erpnext/translations/de.csv | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index d6bceb342d..ca16403d95 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -196,7 +196,7 @@ All other ITC,Alle anderen ITC, All the mandatory Task for employee creation hasn't been done yet.,Alle obligatorischen Aufgaben zur Mitarbeitererstellung wurden noch nicht erledigt., Allocate Payment Amount,Zahlungsbetrag zuweisen, Allocated Amount,Zugewiesene Menge, -Allocated Leaves,Zugewiesene Blätter, +Allocated Leaves,Zugewiesene Urlaubstage, Allocating leaves...,Blätter zuordnen..., Already record exists for the item {0},Es existiert bereits ein Datensatz für den Artikel {0}, "Already set default in pos profile {0} for user {1}, kindly disabled default","Im Standardprofil {0} für den Benutzer {1} ist der Standard bereits festgelegt, standardmäßig deaktiviert", @@ -8623,8 +8623,8 @@ Material Request Warehouse,Materialanforderungslager, Select warehouse for material requests,Wählen Sie Lager für Materialanfragen, Transfer Materials For Warehouse {0},Material für Lager übertragen {0}, Production Plan Material Request Warehouse,Produktionsplan Materialanforderungslager, -Sets 'Source Warehouse' in each row of the items table.,Legt 'Source Warehouse' in jeder Zeile der Artikeltabelle fest., -Sets 'Target Warehouse' in each row of the items table.,"Füllt das Feld ""Ziel Lager"" in allen Positionen der folgenden Tabelle.", +Sets 'Source Warehouse' in each row of the items table.,Legt in jeder Zeile der Artikeltabelle das „Ausgangslager“ fest., +Sets 'Target Warehouse' in each row of the items table.,Legt in jeder Zeile der Artikeltabelle das „Eingangslager“ fest., Show Cancelled Entries,Abgebrochene Einträge anzeigen, Backdated Stock Entry,Backdated Stock Entry, Row #{}: Currency of {} - {} doesn't matches company currency.,Zeile # {}: Die Währung von {} - {} stimmt nicht mit der Firmenwährung überein., @@ -9871,3 +9871,31 @@ Leave Type Allocation,Zuordnung Abwesenheitsarten, From Lead,Aus Lead, From Opportunity,Aus Chance, Publish in Website,Auf Webseite veröffentlichen, +Total Allocated Leave(s),Gesamte zugewiesene Urlaubstage, +Expired Leave(s),Verfallene Urlaubstage, +Used Leave(s),Verbrauchte Urlaubstage, +Leave(s) Pending Approval,Urlaubstage zur Genehmigung ausstehend, +Available Leave(s),Verfügbare Urlaubstage, +Party Specific Item,Parteispezifischer Artikel, +Active Customers,Aktive Kunden, +Annual Sales,Jährlicher Umsatz, +Total Outgoing Bills,Ausgangsrechnungen insgesamt, +Total Incoming Bills,Eingangsrechnungen insgesamt, +Total Incoming Payment,Zahlungseingang insgesamt, +Total Outgoing Payment,Zahlungsausgang insgesamt, +Incoming Bills (Purchase Invoice),Eingehende Rechnungen (Eingangsrechnung), +Outgoing Bills (Sales Invoice),Ausgehende Rechnungen (Ausgangsrechnung), +Accounts Receivable Ageing,Fälligkeit Forderungen, +Accounts Payable Ageing,Fälligkeit Verbindlichkeiten, +Budget Variance,Budgetabweichung, +Based On Value,Basierend auf Wert, +Restrict Items Based On,Artikel einschränken auf Basis von, +Earnings & Deductions,Erträge & Abzüge, +Is Process Loss,Ist Prozessverlust, +Is Finished Item,Ist fertiger Artikel, +Is Scrap Item,Ist Schrott, +Issue a debit note with 0 qty against an existing Sales Invoice,Lastschrift mit Menge 0 gegen eine bestehende Ausgangsrechnung ausstellen, +Show Remarks,Bemerkungen anzeigen, +Website Item,Webseiten-Artikel, +Update Property,Eigenschaft aktualisieren, +Recurring Sales Invoice,Wiederkehrende Ausgangsrechnung, From 5fd468d9ec74cef05a6142c2a1112ee8c6c45ae3 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 25 Aug 2022 11:44:12 +0530 Subject: [PATCH 0098/1047] fix: default supplier not set in the PP --- .../production_plan/production_plan.py | 28 +++++++++++++++++++ .../production_plan/test_production_plan.py | 25 +++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 2cdf8d3ea9..66d458bf75 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -656,6 +656,8 @@ class ProductionPlan(Document): row.idx = idx + 1 self.append("sub_assembly_items", row) + self.set_default_supplier_for_subcontracting_order() + def set_sub_assembly_items_based_on_level(self, row, bom_data, manufacturing_type=None): "Modify bom_data, set additional details." for data in bom_data: @@ -667,6 +669,32 @@ class ProductionPlan(Document): "Subcontract" if data.is_sub_contracted_item else "In House" ) + def set_default_supplier_for_subcontracting_order(self): + items = [ + d.production_item for d in self.sub_assembly_items if d.type_of_manufacturing == "Subcontract" + ] + + if not items: + return + + default_supplier = frappe._dict( + frappe.get_all( + "Item Default", + fields=["parent", "default_supplier"], + filters={"parent": ("in", items), "default_supplier": ("is", "set")}, + as_list=1, + ) + ) + + if not default_supplier: + return + + for row in self.sub_assembly_items: + if row.type_of_manufacturing != "Subcontract": + continue + + row.supplier = default_supplier.get(row.production_item) + def combine_subassembly_items(self, sub_assembly_items_store): "Aggregate if same: Item, Warehouse, Inhouse/Outhouse Manu.g, BOM No." key_wise_data = {} diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index e2415ad848..1d2d1bd9a8 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -281,6 +281,31 @@ class TestProductionPlan(FrappeTestCase): pln.reload() pln.cancel() + def test_production_plan_subassembly_default_supplier(self): + from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom + + bom_tree_1 = {"Test Laptop": {"Test Motherboard": {"Test Motherboard Wires": {}}}} + bom = create_nested_bom(bom_tree_1, prefix="") + + item_doc = frappe.get_doc("Item", "Test Motherboard") + company = "_Test Company" + + item_doc.is_sub_contracted_item = 1 + for row in item_doc.item_defaults: + if row.company == company and not row.default_supplier: + row.default_supplier = "_Test Supplier" + + if not item_doc.item_defaults: + item_doc.append("item_defaults", {"company": company, "default_supplier": "_Test Supplier"}) + + item_doc.save() + + plan = create_production_plan(item_code="Test Laptop", use_multi_level_bom=1, do_not_submit=True) + plan.get_sub_assembly_items() + plan.set_default_supplier_for_subcontracting_order() + + self.assertEqual(plan.sub_assembly_items[0].supplier, "_Test Supplier") + def test_production_plan_combine_subassembly(self): """ Test combining Sub assembly items belonging to the same BOM in Prod Plan. From c1f6dd46d186750146697312d0e10e6ea5c86104 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 25 Aug 2022 12:10:52 +0530 Subject: [PATCH 0099/1047] chore: fix against account --- erpnext/assets/doctype/asset_repair/asset_repair.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index b4316ced62..8758e9c17d 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -249,7 +249,7 @@ class AssetRepair(AccountsController): "account": fixed_asset_account, "debit": item.amount, "debit_in_account_currency": item.amount, - "against": item.expense_account, + "against": item.expense_account or default_expense_account, "voucher_type": self.doctype, "voucher_no": self.name, "cost_center": self.cost_center, From 9ab10def4936424e9825bf0a999a20bb6039d31e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 25 Aug 2022 12:13:17 +0530 Subject: [PATCH 0100/1047] fix: material request connection on work order --- .../doctype/work_order/work_order_dashboard.py | 2 +- .../doctype/material_request/material_request.json | 14 ++++++++++++-- erpnext/stock/doctype/stock_entry/stock_entry.js | 2 ++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py index 465460f95d..d0dcc55932 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py +++ b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py @@ -7,6 +7,6 @@ def get_data(): "non_standard_fieldnames": {"Batch": "reference_name"}, "transactions": [ {"label": _("Transactions"), "items": ["Stock Entry", "Job Card", "Pick List"]}, - {"label": _("Reference"), "items": ["Serial No", "Batch"]}, + {"label": _("Reference"), "items": ["Serial No", "Batch", "Material Request"]}, ], } diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json index cb46a6c368..35931307af 100644 --- a/erpnext/stock/doctype/material_request/material_request.json +++ b/erpnext/stock/doctype/material_request/material_request.json @@ -37,7 +37,8 @@ "tc_name", "terms", "reference", - "job_card" + "job_card", + "work_order" ], "fields": [ { @@ -309,16 +310,24 @@ "label": "Transfer Status", "options": "\nNot Started\nIn Transit\nCompleted", "read_only": 1 + }, + { + "fieldname": "work_order", + "fieldtype": "Link", + "label": "Work Order", + "options": "Work Order", + "read_only": 1 } ], "icon": "fa fa-ticket", "idx": 70, "is_submittable": 1, "links": [], - "modified": "2021-08-17 20:16:12.737743", + "modified": "2022-08-25 11:49:28.155048", "modified_by": "Administrator", "module": "Stock", "name": "Material Request", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { @@ -386,5 +395,6 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "title_field": "title" } \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index e3a8438d95..1bbe570807 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -174,6 +174,8 @@ frappe.ui.form.on('Stock Entry', { if(!items.length) { items = frm.doc.items; } + + mr.work_order = frm.doc.work_order; items.forEach(function(item) { var mr_item = frappe.model.add_child(mr, 'items'); mr_item.item_code = item.item_code; From 8566832dd51bc78d04ae2b0268883b4ae890a98a Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 25 Aug 2022 15:05:13 +0530 Subject: [PATCH 0101/1047] fix: add validation for PO in Stock Entry (#31974) --- erpnext/stock/doctype/stock_entry/stock_entry.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index f719c1efda..c68d1db279 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -116,6 +116,7 @@ class StockEntry(StockController): self.validate_warehouse() self.validate_work_order() self.validate_bom() + self.validate_purchase_order() if self.purpose in ("Manufacture", "Repack"): self.mark_finished_and_scrap_items() @@ -946,6 +947,19 @@ class StockEntry(StockController): item_code = d.original_item or d.item_code validate_bom_no(item_code, d.bom_no) + def validate_purchase_order(self): + if self.purpose == "Send to Subcontractor" and self.get("purchase_order"): + is_old_subcontracting_flow = frappe.db.get_value( + "Purchase Order", self.purchase_order, "is_old_subcontracting_flow" + ) + + if not is_old_subcontracting_flow: + frappe.throw( + _("Please select Subcontracting Order instead of Purchase Order {0}").format( + self.purchase_order + ) + ) + def mark_finished_and_scrap_items(self): if any([d.item_code for d in self.items if (d.is_finished_item and d.t_warehouse)]): return From 6aa8fd0f7b35d3bed60888b1aefec0d817f1c325 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 25 Aug 2022 15:50:06 +0530 Subject: [PATCH 0102/1047] fix: restrict party types to Supplier/Customer for AR/AP report --- .../accounts/report/accounts_receivable/accounts_receivable.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 63242e83fe..f4f2989490 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -731,6 +731,7 @@ class ReceivablePayableReport(object): def prepare_conditions(self): self.qb_selection_filter = [] party_type_field = scrub(self.party_type) + self.qb_selection_filter.append(self.ple.party_type == self.party_type) self.add_common_filters(party_type_field=party_type_field) From 9d02fbadb43d729141422321996084daf31543d1 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Thu, 25 Aug 2022 20:45:35 +0200 Subject: [PATCH 0103/1047] fix: upgrade process to version-14 when currency opportunity wass not set --- .../update_opportunity_currency_fields.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/erpnext/patches/v14_0/update_opportunity_currency_fields.py b/erpnext/patches/v14_0/update_opportunity_currency_fields.py index 076de52619..3a6f1b3a5d 100644 --- a/erpnext/patches/v14_0/update_opportunity_currency_fields.py +++ b/erpnext/patches/v14_0/update_opportunity_currency_fields.py @@ -4,6 +4,8 @@ from frappe.utils import flt import erpnext from erpnext.setup.utils import get_exchange_rate +import click + def execute(): frappe.reload_doctype("Opportunity") @@ -16,6 +18,19 @@ def execute(): for opportunity in opportunities: company_currency = erpnext.get_company_currency(opportunity.company) + if opportunity.currency is None or opportunity.currency == '': + opportunity.currency = company_currency + frappe.db.set_value( + "Opportunity", + opportunity.name, + {"currency": opportunity.currency}, + update_modified=False, + ) + click.secho( + f" Opportunity `{opportunity.name}` has no currency set. We for it to company currency : `{opportunity.currency}`\"\n", + fg="yellow", + ) + # base total and total will be 0 only since item table did not have amount field earlier if opportunity.currency != company_currency: conversion_rate = get_exchange_rate(opportunity.currency, company_currency) @@ -24,6 +39,10 @@ def execute(): conversion_rate = 1 base_opportunity_amount = flt(opportunity.opportunity_amount) + if conversion_rate is None: + print(opportunity.name,conversion_rate,opportunity.currency) + + frappe.db.set_value( "Opportunity", opportunity.name, From ac66538651f3b66c300678a89d7e53642af46e97 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Thu, 25 Aug 2022 22:35:08 +0200 Subject: [PATCH 0104/1047] chore: remove debug --- .../v14_0/update_opportunity_currency_fields.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/erpnext/patches/v14_0/update_opportunity_currency_fields.py b/erpnext/patches/v14_0/update_opportunity_currency_fields.py index 3a6f1b3a5d..7f093ce8c6 100644 --- a/erpnext/patches/v14_0/update_opportunity_currency_fields.py +++ b/erpnext/patches/v14_0/update_opportunity_currency_fields.py @@ -1,11 +1,10 @@ +import click import frappe from frappe.utils import flt import erpnext from erpnext.setup.utils import get_exchange_rate -import click - def execute(): frappe.reload_doctype("Opportunity") @@ -18,7 +17,7 @@ def execute(): for opportunity in opportunities: company_currency = erpnext.get_company_currency(opportunity.company) - if opportunity.currency is None or opportunity.currency == '': + if opportunity.currency is None or opportunity.currency == "": opportunity.currency = company_currency frappe.db.set_value( "Opportunity", @@ -27,7 +26,7 @@ def execute(): update_modified=False, ) click.secho( - f" Opportunity `{opportunity.name}` has no currency set. We for it to company currency : `{opportunity.currency}`\"\n", + f' Opportunity `{opportunity.name}` has no currency set. We for it to company currency : `{opportunity.currency}`"\n', fg="yellow", ) @@ -39,10 +38,6 @@ def execute(): conversion_rate = 1 base_opportunity_amount = flt(opportunity.opportunity_amount) - if conversion_rate is None: - print(opportunity.name,conversion_rate,opportunity.currency) - - frappe.db.set_value( "Opportunity", opportunity.name, From d19b664ba950828c375f312f25356b474b53759b Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Thu, 25 Aug 2022 22:35:44 +0200 Subject: [PATCH 0105/1047] chore: better text --- erpnext/patches/v14_0/update_opportunity_currency_fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v14_0/update_opportunity_currency_fields.py b/erpnext/patches/v14_0/update_opportunity_currency_fields.py index 7f093ce8c6..17df853fb1 100644 --- a/erpnext/patches/v14_0/update_opportunity_currency_fields.py +++ b/erpnext/patches/v14_0/update_opportunity_currency_fields.py @@ -26,7 +26,7 @@ def execute(): update_modified=False, ) click.secho( - f' Opportunity `{opportunity.name}` has no currency set. We for it to company currency : `{opportunity.currency}`"\n', + f' Opportunity `{opportunity.name}` has no currency set. We force it to company currency : `{opportunity.currency}`"\n', fg="yellow", ) From bd4b4ddd8beae9f9bcaa2e6f46d5690ee2fd64c3 Mon Sep 17 00:00:00 2001 From: Solufyin <34390782+Solufyin@users.noreply.github.com> Date: Fri, 26 Aug 2022 11:18:56 +0530 Subject: [PATCH 0106/1047] fix: Purchase Order creation from Sales Order --- erpnext/selling/doctype/sales_order/sales_order.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 8c03cb5b41..09a9652cca 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -892,6 +892,7 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t target.additional_discount_percentage = 0.0 target.discount_amount = 0.0 target.inter_company_order_reference = "" + target.shipping_rule = "" default_price_list = frappe.get_value("Supplier", supplier, "default_price_list") if default_price_list: @@ -1010,6 +1011,7 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None): target.additional_discount_percentage = 0.0 target.discount_amount = 0.0 target.inter_company_order_reference = "" + target.shipping_rule = "" target.customer = "" target.customer_name = "" target.run_method("set_missing_values") From c42fef541a37223e1a7878f7b24a98be8d8df1fc Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 26 Aug 2022 13:15:55 +0530 Subject: [PATCH 0107/1047] chore: remove precision on discount_percentage of Sales Invoice Item --- .../doctype/sales_invoice_item/sales_invoice_item.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index b417c7de03..7cddf123e2 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -282,7 +282,6 @@ "label": "Discount (%) on Price List Rate with Margin", "oldfieldname": "adj_rate", "oldfieldtype": "Float", - "precision": "2", "print_hide": 1 }, { @@ -846,7 +845,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-06-17 05:33:15.335912", + "modified": "2022-08-26 12:06:31.205417", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", From ac571018336c70062b9bcbf182628845eb7ff16d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 26 Aug 2022 17:37:13 +0530 Subject: [PATCH 0108/1047] chore: Update code owners --- CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index b6aadb3f2e..e406f8f56e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -14,8 +14,8 @@ pos* @nextchamp-saqib erpnext/buying/ @rohitwaghchaure @s-aga-r erpnext/maintenance/ @rohitwaghchaure @s-aga-r erpnext/manufacturing/ @rohitwaghchaure @s-aga-r -erpnext/quality_management/ @marination @rohitwaghchaure @s-aga-r -erpnext/stock/ @marination @rohitwaghchaure @s-aga-r +erpnext/quality_management/ @rohitwaghchaure @s-aga-r +erpnext/stock/ @rohitwaghchaure @s-aga-r erpnext/crm/ @NagariaHussain erpnext/education/ @rutwikhdev From af5cbc881f03f93d0bc0b057a13e717a457ea6db Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 26 Aug 2022 22:49:40 +0530 Subject: [PATCH 0109/1047] chore: allow return of components in SCO (#31994) chore: allow return of components in sco --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- .../subcontracting_order/subcontracting_order.js | 6 +++--- .../subcontracting_order/subcontracting_order.py | 11 +++++++++-- .../subcontracting_order_list.js | 1 + .../test_subcontracting_order.py | 15 ++++++++++++++- .../subcontracting_order_supplied_item.json | 5 ++--- .../subcontracting_receipt.json | 4 ++-- 7 files changed, 32 insertions(+), 12 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index c68d1db279..7721efb639 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -2229,7 +2229,7 @@ class StockEntry(StockController): return sorted(list(set(get_serial_nos(self.pro_doc.serial_no)) - set(used_serial_nos))) def update_subcontracting_order_status(self): - if self.subcontracting_order and self.purpose == "Send to Subcontractor": + if self.subcontracting_order and self.purpose in ["Send to Subcontractor", "Material Transfer"]: from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import ( update_subcontracting_order_status, ) diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js index c20f8ab665..bbc58fe29b 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js @@ -107,9 +107,9 @@ frappe.ui.form.on('Subcontracting Order', { get_materials_from_supplier: function (frm) { let sco_rm_details = []; - if (frm.doc.supplied_items && (frm.doc.per_received == 100)) { + if (frm.doc.supplied_items && frm.doc.per_received > 0) { frm.doc.supplied_items.forEach(d => { - if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) { + if (d.total_supplied_qty > 0 && d.total_supplied_qty != d.consumed_qty) { sco_rm_details.push(d.name); } }); @@ -160,7 +160,7 @@ erpnext.buying.SubcontractingOrderController = class SubcontractingOrderControll var me = this; if (doc.docstatus == 1) { - if (doc.status != 'Completed') { + if (!['Closed', 'Completed'].includes(doc.status)) { if (flt(doc.per_received) < 100) { cur_frm.add_custom_button(__('Subcontracting Receipt'), this.make_subcontracting_receipt, __('Create')); if (me.has_unsupplied_items()) { diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index 156f027617..e6de72d494 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -153,7 +153,7 @@ class SubcontractingOrder(SubcontractingController): else: self.set_missing_values() - def update_status(self, status=None, update_modified=False): + def update_status(self, status=None, update_modified=True): if self.docstatus >= 1 and not status: if self.docstatus == 1: if self.status == "Draft": @@ -162,6 +162,10 @@ class SubcontractingOrder(SubcontractingController): status = "Completed" elif self.per_received > 0 and self.per_received < 100: status = "Partially Received" + for item in self.supplied_items: + if item.returned_qty: + status = "Closed" + break else: total_required_qty = total_supplied_qty = 0 for item in self.supplied_items: @@ -176,7 +180,10 @@ class SubcontractingOrder(SubcontractingController): elif self.docstatus == 2: status = "Cancelled" - frappe.db.set_value("Subcontracting Order", self.name, "status", status, update_modified) + if status: + frappe.db.set_value( + "Subcontracting Order", self.name, "status", status, update_modified=update_modified + ) @frappe.whitelist() diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js index 650419cf74..aab2fc927d 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js @@ -10,6 +10,7 @@ frappe.listview_settings['Subcontracting Order'] = { "Completed": "green", "Partial Material Transferred": "purple", "Material Transferred": "blue", + "Closed": "red", }; return [__(doc.status), status_colors[doc.status], "status,=," + doc.status]; }, diff --git a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py index 94bb38e980..098242aed8 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py @@ -7,7 +7,10 @@ import frappe from frappe.tests.utils import FrappeTestCase from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_subcontracting_order -from erpnext.controllers.subcontracting_controller import make_rm_stock_entry +from erpnext.controllers.subcontracting_controller import ( + get_materials_from_supplier, + make_rm_stock_entry, +) from erpnext.controllers.tests.test_subcontracting_controller import ( get_rm_items, get_subcontracting_order, @@ -89,6 +92,16 @@ class TestSubcontractingOrder(FrappeTestCase): sco.load_from_db() self.assertEqual(sco.status, "Partially Received") + # Closed + ste = get_materials_from_supplier(sco.name, [d.name for d in sco.supplied_items]) + ste.save() + ste.submit() + sco.load_from_db() + self.assertEqual(sco.status, "Closed") + ste.cancel() + sco.load_from_db() + self.assertEqual(sco.status, "Partially Received") + # Completed scr = make_subcontracting_receipt(sco.name) scr.save() diff --git a/erpnext/subcontracting/doctype/subcontracting_order_supplied_item/subcontracting_order_supplied_item.json b/erpnext/subcontracting/doctype/subcontracting_order_supplied_item/subcontracting_order_supplied_item.json index a206a21ca6..8f7128be9a 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order_supplied_item/subcontracting_order_supplied_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_order_supplied_item/subcontracting_order_supplied_item.json @@ -150,8 +150,7 @@ "label": "Returned Qty", "no_copy": 1, "print_hide": 1, - "read_only": 1, - "hidden": 1 + "read_only": 1 }, { "fieldname": "total_supplied_qty", @@ -166,7 +165,7 @@ "hide_toolbar": 1, "istable": 1, "links": [], - "modified": "2022-04-07 12:58:28.208847", + "modified": "2022-08-26 16:04:56.125951", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Order Supplied Item", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index 84e95548e1..5cd4e637cc 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -369,7 +369,7 @@ "in_standard_filter": 1, "label": "Status", "no_copy": 1, - "options": "\nDraft\nCompleted\nReturn\nReturn Issued\nCancelled", + "options": "\nDraft\nCompleted\nReturn\nReturn Issued\nCancelled\nClosed", "print_hide": 1, "print_width": "150px", "read_only": 1, @@ -628,7 +628,7 @@ "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2022-08-22 17:30:40.827517", + "modified": "2022-08-26 21:02:26.353870", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", From 318da16b99232033b592cc6e59f931a360758b1d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 29 Aug 2022 14:18:39 +0530 Subject: [PATCH 0110/1047] fix: Rounded total for cash and non trade discount invoices --- erpnext/controllers/taxes_and_totals.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index bc38d08b80..9dbcdb04c5 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -41,6 +41,7 @@ class calculate_taxes_and_totals(object): if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"): self.doc.grand_total -= self.doc.discount_amount self.doc.base_grand_total -= self.doc.base_discount_amount + self.set_rounded_total() self.calculate_shipping_charges() From 69ffef8c0ee728779dfb76b5eee4f15ef3421b6d Mon Sep 17 00:00:00 2001 From: MOHAMMED NIYAS <76736615+niyazrazak@users.noreply.github.com> Date: Mon, 29 Aug 2022 14:47:43 +0530 Subject: [PATCH 0111/1047] fix: lost quotation not to expired --- erpnext/selling/doctype/quotation/quotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 863fbc4059..96092b1523 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -268,7 +268,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): def set_expired_status(): # filter out submitted non expired quotations whose validity has been ended - cond = "`tabQuotation`.docstatus = 1 and `tabQuotation`.status != 'Expired' and `tabQuotation`.valid_till < %s" + cond = "`tabQuotation`.docstatus = 1 and `tabQuotation`.status NOT IN ('Expired', 'Lost') and `tabQuotation`.valid_till < %s" # check if those QUO have SO against it so_against_quo = """ SELECT From 9dbaaa33f5ec7df8904e71e305c26a5c904e2028 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Mon, 29 Aug 2022 15:07:20 +0530 Subject: [PATCH 0112/1047] fix: AD not getting copied from SCO while creating a SE (#32004) --- .../stock/doctype/stock_entry/stock_entry.py | 40 ++++-- .../subcontracting_order.js | 128 ++---------------- 2 files changed, 38 insertions(+), 130 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 7721efb639..d70952282d 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -2568,27 +2568,26 @@ def get_supplied_items( @frappe.whitelist() def get_items_from_subcontracting_order(source_name, target_doc=None): - sco = frappe.get_doc("Subcontracting Order", source_name) + def post_process(source, target): + target.stock_entry_type = target.purpose = "Send to Subcontractor" + target.subcontracting_order = source_name - if sco.docstatus == 1: - if target_doc and isinstance(target_doc, str): - target_doc = frappe.get_doc(json.loads(target_doc)) - - if target_doc.items: - target_doc.items = [] + if target.items: + target.items = [] warehouses = {} - for item in sco.items: + for item in source.items: warehouses[item.name] = item.warehouse - for item in sco.supplied_items: - target_doc.append( + for item in source.supplied_items: + target.append( "items", { "s_warehouse": warehouses.get(item.reference_name), - "t_warehouse": sco.supplier_warehouse, + "t_warehouse": source.supplier_warehouse, + "subcontracted_item": item.main_item_code, "item_code": item.rm_item_code, - "qty": item.required_qty, + "qty": max(item.required_qty - item.total_supplied_qty, 0), "transfer_qty": item.required_qty, "uom": item.stock_uom, "stock_uom": item.stock_uom, @@ -2596,6 +2595,23 @@ def get_items_from_subcontracting_order(source_name, target_doc=None): }, ) + target_doc = get_mapped_doc( + "Subcontracting Order", + source_name, + { + "Subcontracting Order": { + "doctype": "Stock Entry", + "field_no_map": ["purchase_order"], + "validation": { + "docstatus": ["=", 1], + }, + }, + }, + target_doc, + post_process, + ignore_child_tables=True, + ) + return target_doc diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js index bbc58fe29b..065ef39db3 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js @@ -164,10 +164,7 @@ erpnext.buying.SubcontractingOrderController = class SubcontractingOrderControll if (flt(doc.per_received) < 100) { cur_frm.add_custom_button(__('Subcontracting Receipt'), this.make_subcontracting_receipt, __('Create')); if (me.has_unsupplied_items()) { - cur_frm.add_custom_button(__('Material to Supplier'), - () => { - me.make_stock_entry(); - }, __('Transfer')); + cur_frm.add_custom_button(__('Material to Supplier'), this.make_stock_entry, __('Transfer')); } } cur_frm.page.set_inner_btn_group_as_primary(__('Create')); @@ -195,120 +192,6 @@ erpnext.buying.SubcontractingOrderController = class SubcontractingOrderControll transaction_controller.autofill_warehouse(child_table, warehouse_field, warehouse); } - make_stock_entry() { - var items = $.map(cur_frm.doc.items, (d) => d.bom ? d.item_code : false); - var me = this; - - if (items.length >= 1) { - me.raw_material_data = []; - me.show_dialog = 1; - let title = __('Transfer Material to Supplier'); - let fields = [ - { fieldtype: 'Section Break', label: __('Raw Materials') }, - { - fieldname: 'sub_con_rm_items', fieldtype: 'Table', label: __('Items'), - fields: [ - { - fieldtype: 'Data', - fieldname: 'item_code', - label: __('Item'), - read_only: 1, - in_list_view: 1 - }, - { - fieldtype: 'Data', - fieldname: 'rm_item_code', - label: __('Raw Material'), - read_only: 1, - in_list_view: 1 - }, - { - fieldtype: 'Float', - read_only: 1, - fieldname: 'qty', - label: __('Quantity'), - in_list_view: 1 - }, - { - fieldtype: 'Data', - read_only: 1, - fieldname: 'warehouse', - label: __('Reserve Warehouse'), - in_list_view: 1 - }, - { - fieldtype: 'Float', - read_only: 1, - fieldname: 'rate', - label: __('Rate'), - hidden: 1 - }, - { - fieldtype: 'Float', - read_only: 1, - fieldname: 'amount', - label: __('Amount'), - hidden: 1 - }, - { - fieldtype: 'Link', - read_only: 1, - fieldname: 'uom', - label: __('UOM'), - hidden: 1 - } - ], - data: me.raw_material_data, - get_data: () => me.raw_material_data - } - ]; - - me.dialog = new frappe.ui.Dialog({ - title: title, fields: fields - }); - - if (me.frm.doc['supplied_items']) { - me.frm.doc['supplied_items'].forEach((item) => { - if (item.rm_item_code && item.main_item_code && item.required_qty - item.supplied_qty != 0) { - me.raw_material_data.push({ - 'name': item.name, - 'item_code': item.main_item_code, - 'rm_item_code': item.rm_item_code, - 'item_name': item.rm_item_code, - 'qty': item.required_qty - item.supplied_qty, - 'warehouse': item.reserve_warehouse, - 'rate': item.rate, - 'amount': item.amount, - 'stock_uom': item.stock_uom - }); - me.dialog.fields_dict.sub_con_rm_items.grid.refresh(); - } - }); - } - - me.dialog.get_field('sub_con_rm_items').check_all_rows(); - - me.dialog.show(); - this.dialog.set_primary_action(__('Transfer'), () => { - me.values = me.dialog.get_values(); - 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) { - 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()); - me.dialog.hide(); - } - }); - } - - me.dialog.get_close_btn().on('click', () => { - me.dialog.hide(); - }); - } - has_unsupplied_items() { return this.frm.doc['supplied_items'].some(item => item.required_qty > item.supplied_qty); } @@ -321,6 +204,15 @@ erpnext.buying.SubcontractingOrderController = class SubcontractingOrderControll }); } + make_stock_entry() { + frappe.model.open_mapped_doc({ + method: 'erpnext.stock.doctype.stock_entry.stock_entry.get_items_from_subcontracting_order', + source_name: cur_frm.doc.name, + freeze: true, + freeze_message: __('Creating Stock Entry ...') + }); + } + make_rm_stock_entry(rm_items) { frappe.call({ method: 'erpnext.controllers.subcontracting_controller.make_rm_stock_entry', From 5782c4469ac98b691564af8f1e38330934584558 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 22 Aug 2022 15:46:04 +0530 Subject: [PATCH 0113/1047] refactor: re-add remarks field to payment ledger and AR/AP report --- .../payment_ledger_entry/payment_ledger_entry.json | 10 ++++++++-- .../report/accounts_receivable/accounts_receivable.js | 5 +++++ .../report/accounts_receivable/accounts_receivable.py | 5 +++++ erpnext/accounts/utils.py | 1 + 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json index 4596b00fc1..22842cec0f 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json @@ -22,7 +22,8 @@ "amount", "account_currency", "amount_in_account_currency", - "delinked" + "delinked", + "remarks" ], "fields": [ { @@ -136,12 +137,17 @@ "fieldtype": "Link", "label": "Finance Book", "options": "Finance Book" + }, + { + "fieldname": "remarks", + "fieldtype": "Text", + "label": "Remarks" } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2022-07-11 09:13:54.379168", + "modified": "2022-08-22 15:32:56.629430", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Ledger Entry", diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 0238711a70..0b4e577f6c 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -178,6 +178,11 @@ frappe.query_reports["Accounts Receivable"] = { "fieldtype": "Data", "hidden": 1 }, + { + "fieldname": "show_remarks", + "label": __("Show Remarks"), + "fieldtype": "Check", + }, { "fieldname": "customer_name", "label": __("Customer Name"), diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 63242e83fe..90a431d63c 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -119,6 +119,7 @@ class ReceivablePayableReport(object): party_account=ple.account, posting_date=ple.posting_date, account_currency=ple.account_currency, + remarks=ple.remarks, invoiced=0.0, paid=0.0, credit_note=0.0, @@ -697,6 +698,7 @@ class ReceivablePayableReport(object): ple.account_currency, ple.amount, ple.amount_in_account_currency, + ple.remarks, ) .where(ple.delinked == 0) .where(Criterion.all(self.qb_selection_filter)) @@ -974,6 +976,9 @@ class ReceivablePayableReport(object): options="Supplier Group", ) + if self.filters.show_remarks: + self.add_column(label=_("Remarks"), fieldname="remarks", fieldtype="Text", width=200), + def add_column(self, label, fieldname=None, fieldtype="Currency", options=None, width=120): if not fieldname: fieldname = scrub(label) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 018e8f9301..f61e8ac960 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1424,6 +1424,7 @@ def create_payment_ledger_entry( "amount": dr_or_cr, "amount_in_account_currency": dr_or_cr_account_currency, "delinked": True if cancel else False, + "remarks": gle.remarks, } ) From 3a6b095ed45fbc47628e4b189e1d1a89e17ffc25 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 23 Aug 2022 15:10:55 +0530 Subject: [PATCH 0114/1047] chore: patch for migrating remarks to payment ledger --- ...grate_remarks_from_gl_to_payment_ledger.py | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py diff --git a/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py b/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py new file mode 100644 index 0000000000..062d24b78b --- /dev/null +++ b/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py @@ -0,0 +1,56 @@ +import frappe +from frappe import qb +from frappe.utils import create_batch + + +def execute(): + if frappe.reload_doc("accounts", "doctype", "payment_ledger_entry"): + + gle = qb.DocType("GL Entry") + ple = qb.DocType("Payment Ledger Entry") + + # get ple and their remarks from GL Entry + pl_entries = ( + qb.from_(ple) + .left_join(gle) + .on( + (ple.account == gle.account) + & (ple.party_type == gle.party_type) + & (ple.party == gle.party) + & (ple.voucher_type == gle.voucher_type) + & (ple.voucher_no == gle.voucher_no) + & (ple.company == gle.company) + ) + .select( + ple.company, + ple.account, + ple.party_type, + ple.party, + ple.voucher_type, + ple.voucher_no, + gle.remarks.as_("gle_remarks"), + ) + .where((ple.delinked == 0) & (gle.is_cancelled == 0)) + .run(as_dict=True) + ) + + if pl_entries: + # split into multiple batches, update and commit for each batch + batch_size = 1000 + for batch in create_batch(pl_entries, batch_size): + for entry in batch: + query = ( + qb.update(ple) + .set(ple.remarks, entry.gle_remarks) + .where( + (ple.company == entry.company) + & (ple.account == entry.account) + & (ple.party_type == entry.party_type) + & (ple.party == entry.party) + & (ple.voucher_type == entry.voucher_type) + & (ple.voucher_no == entry.voucher_no) + ) + ) + query.run() + + frappe.db.commit() From d522f13d556af53ba6b6cb9c833e14aefc0226cb Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 29 Aug 2022 15:31:26 +0530 Subject: [PATCH 0115/1047] chore: add remarks migration to patches.txt --- erpnext/patches.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d92353aca4..4729add16b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -311,4 +311,5 @@ erpnext.patches.v14_0.remove_india_localisation # 14-07-2022 erpnext.patches.v13_0.fix_number_and_frequency_for_monthly_depreciation erpnext.patches.v14_0.remove_hr_and_payroll_modules # 20-07-2022 erpnext.patches.v14_0.fix_crm_no_of_employees -erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes \ No newline at end of file +erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes +erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger From 2d41704424899a8cc1992a9c379a5d340effbce8 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 29 Aug 2022 20:50:27 +0530 Subject: [PATCH 0116/1047] fix(patch): update sla doctype directly (#32014) fix: update sla doctype directly --- erpnext/patches/v13_0/add_doctype_to_sla.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/add_doctype_to_sla.py b/erpnext/patches/v13_0/add_doctype_to_sla.py index 5f5974f65d..2d3b0de5b5 100644 --- a/erpnext/patches/v13_0/add_doctype_to_sla.py +++ b/erpnext/patches/v13_0/add_doctype_to_sla.py @@ -14,7 +14,8 @@ def execute(): for sla in frappe.get_all("Service Level Agreement"): agreement = frappe.get_doc("Service Level Agreement", sla.name) - agreement.document_type = "Issue" + agreement.db_set("document_type", "Issue") + agreement.reload() agreement.apply_sla_for_resolution = 1 agreement.append("sla_fulfilled_on", {"status": "Resolved"}) agreement.append("sla_fulfilled_on", {"status": "Closed"}) From 73f4d5931d881b1eacc4fde89affd291f88c5ef1 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Mon, 29 Aug 2022 18:26:07 +0200 Subject: [PATCH 0117/1047] fix: permissions for Task Type (#32016) --- .../projects/doctype/task_type/task_type.json | 123 +++++------------- 1 file changed, 33 insertions(+), 90 deletions(-) diff --git a/erpnext/projects/doctype/task_type/task_type.json b/erpnext/projects/doctype/task_type/task_type.json index 3254444a48..b04264e9c7 100644 --- a/erpnext/projects/doctype/task_type/task_type.json +++ b/erpnext/projects/doctype/task_type/task_type.json @@ -1,127 +1,70 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, + "actions": [], "autoname": "Prompt", - "beta": 0, "creation": "2019-04-19 15:04:05.317138", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "weight", + "description" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "weight", "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": "Weight", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Weight" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "description", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Description" } ], - "has_web_view": 0, - "hide_toolbar": 0, - "idx": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-04-19 15:31:48.080164", + "links": [], + "modified": "2022-08-29 17:46:41.342979", "modified_by": "Administrator", "module": "Projects", "name": "Task Type", - "name_case": "", + "naming_rule": "Set by user", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Projects Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Projects User", + "share": 1 } ], "quick_entry": 1, - "read_only": 0, - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "ASC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "states": [], + "track_changes": 1 } \ No newline at end of file From ffa3071d36e62eb721bc9a3105fb7af4b93cf8fc Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 30 Aug 2022 15:43:57 +0530 Subject: [PATCH 0118/1047] fix: force delete old report docs (#32026) --- erpnext/patches/v13_0/delete_old_sales_reports.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v13_0/delete_old_sales_reports.py b/erpnext/patches/v13_0/delete_old_sales_reports.py index b31c9d17d7..1b53da755c 100644 --- a/erpnext/patches/v13_0/delete_old_sales_reports.py +++ b/erpnext/patches/v13_0/delete_old_sales_reports.py @@ -16,18 +16,18 @@ def execute(): delete_auto_email_reports(report) check_and_delete_linked_reports(report) - frappe.delete_doc("Report", report) + frappe.delete_doc("Report", report, force=True) def delete_auto_email_reports(report): """Check for one or multiple Auto Email Reports and delete""" auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"]) for auto_email_report in auto_email_reports: - frappe.delete_doc("Auto Email Report", auto_email_report[0]) + frappe.delete_doc("Auto Email Report", auto_email_report[0], force=True) def delete_links_from_desktop_icons(report): """Check for one or multiple Desktop Icons and delete""" desktop_icons = frappe.db.get_values("Desktop Icon", {"_report": report}, ["name"]) for desktop_icon in desktop_icons: - frappe.delete_doc("Desktop Icon", desktop_icon[0]) + frappe.delete_doc("Desktop Icon", desktop_icon[0], force=True) From eefc9b71725d5530e3bd259a299d3c0673385a4a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 30 Aug 2022 19:16:36 +0530 Subject: [PATCH 0119/1047] fix: Loan Interest accruals for 0 rated loans --- .../loan_interest_accrual/loan_interest_accrual.py | 1 - .../doctype/loan_repayment/loan_repayment.py | 1 + .../process_loan_interest_accrual.py | 13 ++++++++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 6d62aefdca..9a6bcd4daa 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -236,7 +236,6 @@ def get_term_loans(date, term_loan=None, loan_type=None): AND l.is_term_loan =1 AND rs.payment_date <= %s AND rs.is_accrued=0 {0} - AND rs.interest_amount > 0 AND l.status = 'Disbursed' ORDER BY rs.payment_date""".format( condition diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 29da988ce4..018832c7d7 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -735,6 +735,7 @@ def get_amounts(amounts, against_loan, posting_date): ) amounts["pending_accrual_entries"] = pending_accrual_entries amounts["unaccrued_interest"] = flt(unaccrued_interest, precision) + amounts["written_off_amount"] = flt(against_loan_doc.written_off_amount, precision) if final_due_date: amounts["due_date"] = final_due_date diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py index 81464a36c3..25c72d91a7 100644 --- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py @@ -57,7 +57,7 @@ def process_loan_interest_accrual_for_demand_loans( def process_loan_interest_accrual_for_term_loans(posting_date=None, loan_type=None, loan=None): - if not term_loan_accrual_pending(posting_date or nowdate()): + if not term_loan_accrual_pending(posting_date or nowdate(), loan=loan): return loan_process = frappe.new_doc("Process Loan Interest Accrual") @@ -71,9 +71,12 @@ def process_loan_interest_accrual_for_term_loans(posting_date=None, loan_type=No return loan_process.name -def term_loan_accrual_pending(date): - pending_accrual = frappe.db.get_value( - "Repayment Schedule", {"payment_date": ("<=", date), "is_accrued": 0} - ) +def term_loan_accrual_pending(date, loan=None): + filters = {"payment_date": ("<=", date), "is_accrued": 0} + + if loan: + filters.update({"parent": loan}) + + pending_accrual = frappe.db.get_value("Repayment Schedule", filters) return pending_accrual From a76d3827ec5355b7eecedc3e66bfd85b119d5211 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 30 Aug 2022 19:24:57 +0530 Subject: [PATCH 0120/1047] chore: Add check for principal amount --- .../doctype/loan_interest_accrual/loan_interest_accrual.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 9a6bcd4daa..cac3f1f0f3 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -236,6 +236,7 @@ def get_term_loans(date, term_loan=None, loan_type=None): AND l.is_term_loan =1 AND rs.payment_date <= %s AND rs.is_accrued=0 {0} + AND rs.principal_amount > 0 AND l.status = 'Disbursed' ORDER BY rs.payment_date""".format( condition From 4a38ce659d4da7400fd219060cbcdd77ce6ccf1b Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 22 Aug 2022 00:23:22 +0530 Subject: [PATCH 0121/1047] refactor!: drop redisearch incr: replace text and tag fields incr: use rediswrapper's make key incr: indexDefinition from redis incr: replace index creation incr: replace AutoCompleter incr: replace product search ac incr: replace client querying fix: broken redisearch load test fix: pass actual query to get suggestion --- erpnext/e_commerce/redisearch_utils.py | 58 ++++++++++++----------- erpnext/templates/pages/product_search.py | 21 ++++---- pyproject.toml | 1 - 3 files changed, 41 insertions(+), 39 deletions(-) diff --git a/erpnext/e_commerce/redisearch_utils.py b/erpnext/e_commerce/redisearch_utils.py index 1f649c7b48..87ca9bd83d 100644 --- a/erpnext/e_commerce/redisearch_utils.py +++ b/erpnext/e_commerce/redisearch_utils.py @@ -7,7 +7,9 @@ import frappe from frappe import _ from frappe.utils.redis_wrapper import RedisWrapper from redis import ResponseError -from redisearch import AutoCompleter, Client, IndexDefinition, Suggestion, TagField, TextField +from redis.commands.search.field import TagField, TextField +from redis.commands.search.indexDefinition import IndexDefinition +from redis.commands.search.suggestion import Suggestion WEBSITE_ITEM_INDEX = "website_items_index" WEBSITE_ITEM_KEY_PREFIX = "website_item:" @@ -35,12 +37,9 @@ def is_redisearch_enabled(): def is_search_module_loaded(): try: cache = frappe.cache() - out = cache.execute_command("MODULE LIST") - - parsed_output = " ".join( - (" ".join([frappe.as_unicode(s) for s in o if not isinstance(s, int)]) for o in out) - ) - return "search" in parsed_output + for module in cache.module_list(): + if module.get(b"name") == b"search": + return True except Exception: return False # handling older redis versions @@ -58,18 +57,18 @@ def if_redisearch_enabled(function): def make_key(key): - return "{0}|{1}".format(frappe.conf.db_name, key).encode("utf-8") + return frappe.cache().make_key(key) @if_redisearch_enabled def create_website_items_index(): "Creates Index Definition." - # CREATE index - client = Client(make_key(WEBSITE_ITEM_INDEX), conn=frappe.cache()) + redis = frappe.cache() + index = redis.ft(WEBSITE_ITEM_INDEX) try: - client.drop_index() # drop if already exists + index.dropindex() # drop if already exists except ResponseError: # will most likely raise a ResponseError if index does not exist # ignore and create index @@ -86,9 +85,10 @@ def create_website_items_index(): if "web_item_name" in idx_fields: idx_fields.remove("web_item_name") - idx_fields = list(map(to_search_field, idx_fields)) + idx_fields = [to_search_field(f) for f in idx_fields] - client.create_index( + # TODO: sortable? + index.create_index( [TextField("web_item_name", sortable=True)] + idx_fields, definition=idx_def, ) @@ -119,8 +119,8 @@ def insert_item_to_index(website_item_doc): @if_redisearch_enabled def insert_to_name_ac(web_name, doc_name): - ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=frappe.cache()) - ac.add_suggestions(Suggestion(web_name, payload=doc_name)) + ac = frappe.cache().ft() + ac.sugadd(WEBSITE_ITEM_NAME_AUTOCOMPLETE, Suggestion(web_name, payload=doc_name)) def create_web_item_map(website_item_doc): @@ -157,9 +157,8 @@ def delete_item_from_index(website_item_doc): @if_redisearch_enabled def delete_from_ac_dict(website_item_doc): """Removes this items's name from autocomplete dictionary""" - cache = frappe.cache() - name_ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=cache) - name_ac.delete(website_item_doc.web_item_name) + ac = frappe.cache().ft() + ac.sugdel(website_item_doc.web_item_name) @if_redisearch_enabled @@ -170,8 +169,6 @@ def define_autocomplete_dictionary(): """ cache = frappe.cache() - item_ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=cache) - item_group_ac = AutoCompleter(make_key(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE), conn=cache) # Delete both autocomplete dicts try: @@ -180,38 +177,43 @@ def define_autocomplete_dictionary(): except Exception: raise_redisearch_error() - create_items_autocomplete_dict(autocompleter=item_ac) - create_item_groups_autocomplete_dict(autocompleter=item_group_ac) + create_items_autocomplete_dict() + create_item_groups_autocomplete_dict() @if_redisearch_enabled -def create_items_autocomplete_dict(autocompleter): +def create_items_autocomplete_dict(): "Add items as suggestions in Autocompleter." + + ac = frappe.cache().ft() items = frappe.get_all( "Website Item", fields=["web_item_name", "item_group"], filters={"published": 1} ) - for item in items: - autocompleter.add_suggestions(Suggestion(item.web_item_name)) + ac.sugadd(WEBSITE_ITEM_NAME_AUTOCOMPLETE, Suggestion(item.web_item_name)) @if_redisearch_enabled -def create_item_groups_autocomplete_dict(autocompleter): +def create_item_groups_autocomplete_dict(): "Add item groups with weightage as suggestions in Autocompleter." + published_item_groups = frappe.get_all( "Item Group", fields=["name", "route", "weightage"], filters={"show_in_website": 1} ) if not published_item_groups: return + ac = frappe.cache().ft() + for item_group in published_item_groups: payload = json.dumps({"name": item_group.name, "route": item_group.route}) - autocompleter.add_suggestions( + ac.sugadd( + WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE, Suggestion( string=item_group.name, score=frappe.utils.flt(item_group.weightage) or 1.0, payload=payload, # additional info that can be retrieved later - ) + ), ) diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py index 0768cc3fa6..f40fd479f4 100644 --- a/erpnext/templates/pages/product_search.py +++ b/erpnext/templates/pages/product_search.py @@ -5,14 +5,13 @@ import json import frappe from frappe.utils import cint, cstr -from redisearch import AutoCompleter, Client, Query +from redis.commands.search.query import Query from erpnext.e_commerce.redisearch_utils import ( WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE, WEBSITE_ITEM_INDEX, WEBSITE_ITEM_NAME_AUTOCOMPLETE, is_redisearch_enabled, - make_key, ) from erpnext.e_commerce.shopping_cart.product_info import set_product_info_for_website from erpnext.setup.doctype.item_group.item_group import get_item_for_list_in_html @@ -88,15 +87,17 @@ def product_search(query, limit=10, fuzzy_search=True): if not query: return search_results - red = frappe.cache() + redis = frappe.cache() query = clean_up_query(query) # TODO: Check perf/correctness with Suggestions & Query vs only Query # TODO: Use Levenshtein Distance in Query (max=3) - ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=red) - client = Client(make_key(WEBSITE_ITEM_INDEX), conn=red) - suggestions = ac.get_suggestions( - query, num=limit, fuzzy=fuzzy_search and len(query) > 3 # Fuzzy on length < 3 can be real slow + redisearch = redis.ft(WEBSITE_ITEM_INDEX) + suggestions = redisearch.sugget( + WEBSITE_ITEM_NAME_AUTOCOMPLETE, + query, + num=limit, + fuzzy=fuzzy_search and len(query) > 3, ) # Build a query @@ -106,8 +107,8 @@ def product_search(query, limit=10, fuzzy_search=True): query_string += f"|('{clean_up_query(s.string)}')" q = Query(query_string) + results = redisearch.search(q) - results = client.search(q) search_results["results"] = list(map(convert_to_dict, results.docs)) search_results["results"] = sorted( search_results["results"], key=lambda k: frappe.utils.cint(k["ranking"]), reverse=True @@ -141,8 +142,8 @@ def get_category_suggestions(query): if not query: return search_results - ac = AutoCompleter(make_key(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE), conn=frappe.cache()) - suggestions = ac.get_suggestions(query, num=10, with_payloads=True) + ac = frappe.cache().ft() + suggestions = ac.sugget(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE, query, num=10, with_payloads=True) results = [json.loads(s.payload) for s in suggestions] diff --git a/pyproject.toml b/pyproject.toml index 5acfd39272..14684f3491 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,6 @@ dependencies = [ "pycountry~=20.7.3", "python-stdnum~=1.16", "Unidecode~=1.2.0", - "redisearch~=2.1.0", # integration dependencies "gocardless-pro~=1.22.0", From 30039e8e624f341f1f32b845094d1a1e96e29799 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 31 Aug 2022 15:38:05 +0530 Subject: [PATCH 0122/1047] fix: encode thumbnail URL If it contains space the URL won't load --- erpnext/e_commerce/product_ui/search.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/e_commerce/product_ui/search.js b/erpnext/e_commerce/product_ui/search.js index 61922459e5..1688cc1fb6 100644 --- a/erpnext/e_commerce/product_ui/search.js +++ b/erpnext/e_commerce/product_ui/search.js @@ -200,7 +200,7 @@ erpnext.ProductSearch = class { let thumbnail = res.thumbnail || '/assets/erpnext/images/ui-states/cart-empty-state.png'; html += `
{% if enabled_checkout and ((doc.doctype=="Sales Order" and doc.per_billed <= 0) - or (doc.doctype=="Sales Invoice" and doc.outstanding_amount > 0)) %} + or (doc.doctype=="Sales Invoice" and doc.outstanding_amount> 0)) %}
-
-
-
- Payment -
-
-
{% if available_loyalty_points %} +
+
+
+ Loyalty Points +
+
+
+
Enter Loyalty Points
- +
-

Available Points: {{ available_loyalty_points }}

+

Available Points: {{ + available_loyalty_points }}

{% endif %}
- - -
-
@@ -172,17 +182,17 @@
{% endif %} -
{% if doc.terms %}
-

{{ doc.terms }}

+
+

{{ doc.terms }}

{% endif %} {% endblock %} {% block script %} - + -{% endblock %} +{% endblock %} \ No newline at end of file From 518ab93e039d68827506c3ac92db3c09aea644e3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 4 Oct 2022 12:35:59 +0530 Subject: [PATCH 0298/1047] refactor: remove duplicate entries on remarks migration patch --- ...grate_remarks_from_gl_to_payment_ledger.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py b/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py index 062d24b78b..fd2a2a39cc 100644 --- a/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py +++ b/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py @@ -3,6 +3,29 @@ from frappe import qb from frappe.utils import create_batch +def remove_duplicate_entries(pl_entries): + unique_vouchers = set() + for x in pl_entries: + unique_vouchers.add( + (x.company, x.account, x.party_type, x.party, x.voucher_type, x.voucher_no, x.gle_remarks) + ) + + entries = [] + for x in unique_vouchers: + entries.append( + frappe._dict( + company=x[0], + account=x[1], + party_type=x[2], + party=x[3], + voucher_type=x[4], + voucher_no=x[5], + gle_remarks=x[6], + ) + ) + return entries + + def execute(): if frappe.reload_doc("accounts", "doctype", "payment_ledger_entry"): @@ -34,6 +57,8 @@ def execute(): .run(as_dict=True) ) + pl_entries = remove_duplicate_entries(pl_entries) + if pl_entries: # split into multiple batches, update and commit for each batch batch_size = 1000 From 39707757a6a175a3f89a44e1d13ffde9a66420b0 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Tue, 4 Oct 2022 22:01:50 +0530 Subject: [PATCH 0299/1047] chore: add `Manual Inspection` field in Quality Inspection DocType --- .../quality_inspection/quality_inspection.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json index edfe7e98b2..db9322f326 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json @@ -10,6 +10,7 @@ "naming_series", "report_date", "status", + "manual_inspection", "column_break_4", "inspection_type", "reference_type", @@ -231,6 +232,12 @@ "label": "Status", "options": "\nAccepted\nRejected", "reqd": 1 + }, + { + "default": "0", + "fieldname": "manual_inspection", + "fieldtype": "Check", + "label": "Manual Inspection" } ], "icon": "fa fa-search", @@ -238,10 +245,11 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-12-18 19:59:55.710300", + "modified": "2022-10-04 22:00:13.995221", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { @@ -262,5 +270,6 @@ "search_fields": "item_code, report_date, reference_name", "show_name_in_global_search": 1, "sort_field": "modified", - "sort_order": "ASC" + "sort_order": "ASC", + "states": [] } \ No newline at end of file From aaabba9b1e93ab90fd2afa9fb7b404b10fe8a3a4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 5 Oct 2022 10:58:21 +0530 Subject: [PATCH 0300/1047] fix: TooManyWritesError during reposting of stock --- .../doctype/repost_item_valuation/repost_item_valuation.py | 3 +++ 1 file changed, 3 insertions(+) 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 c4705246b3..d6f9bae5da 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -128,6 +128,9 @@ def repost(doc): if not frappe.db.exists("Repost Item Valuation", doc.name): return + # This is to avoid TooManyWritesError in case of large reposts + frappe.db.MAX_WRITES_PER_TRANSACTION *= 4 + doc.set_status("In Progress") if not frappe.flags.in_test: frappe.db.commit() From 0e4017cbe51394986046818946d8f6ac39798182 Mon Sep 17 00:00:00 2001 From: HENRY Florian Date: Wed, 5 Oct 2022 14:49:44 +0200 Subject: [PATCH 0301/1047] chore: update fr translation (#32385) --- erpnext/translations/fr.csv | 61 +++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv index b2074618a6..7989bf7ebc 100644 --- a/erpnext/translations/fr.csv +++ b/erpnext/translations/fr.csv @@ -785,7 +785,7 @@ Default BOM for {0} not found,Nomenclature par défaut {0} introuvable, Default BOM not found for Item {0} and Project {1},La nomenclature par défaut n'a pas été trouvée pour l'Article {0} et le Projet {1}, Default Letter Head,En-Tête de Courrier par Défaut, Default Tax Template,Modèle de Taxes par Défaut, -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.,L’Unité de Mesure par Défaut pour l’Article {0} ne peut pas être modifiée directement parce que vous avez déjà fait une (des) transaction (s) avec une autre unité de mesure. Vous devez créer un nouvel article pour utiliser une UDM par défaut différente., +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.,L’Unité de Mesure par Défaut pour l’Article {0} ne peut pas être modifiée directement parce que vous avez déjà fait une (des) transaction (s) avec une autre unité de mesure. Vous devez créer un nouvel article pour utiliser une UdM par défaut différente., Default Unit of Measure for Variant '{0}' must be same as in Template '{1}',L’Unité de mesure par défaut pour la variante '{0}' doit être la même que dans le Modèle '{1}', Default settings for buying transactions.,Paramètres par défaut pour les transactions d'achat., Default settings for selling transactions.,Paramètres par défaut pour les transactions de vente., @@ -838,7 +838,7 @@ Difference Account,Compte d’Écart, "Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry","Le Compte d’Écart doit être un compte de type Actif / Passif, puisque cette Réconciliation de Stock est une écriture d'à-nouveau", Difference Amount,Écart de Montant, Difference Amount must be zero,L’Écart de Montant doit être égal à zéro, -Different UOM for items will lead to incorrect (Total) Net Weight value. Make sure that Net Weight of each item is in the same UOM.,Différentes UDM pour les articles conduira à un Poids Net (Total) incorrect . Assurez-vous que le Poids Net de chaque article a la même unité de mesure ., +Different UOM for items will lead to incorrect (Total) Net Weight value. Make sure that Net Weight of each item is in the same UOM.,Différentes UdM pour les articles conduira à un Poids Net (Total) incorrect . Assurez-vous que le Poids Net de chaque article a la même unité de mesure ., Direct Expenses,Charges Directes, Direct Income,Revenu direct, Disable,Désactiver, @@ -1409,7 +1409,7 @@ Lab Test,Test de laboratoire, Lab Test Report,Rapport de test de laboratoire, Lab Test Sample,Échantillon de test de laboratoire, Lab Test Template,Modèle de test de laboratoire, -Lab Test UOM,UDM de test de laboratoire, +Lab Test UOM,UdM de test de laboratoire, Lab Tests and Vital Signs,Tests de laboratoire et signes vitaux, Lab result datetime cannot be before testing datetime,La date et l'heure du résultat de laboratoire ne peuvent pas être avant la date et l'heure du test, Lab testing datetime cannot be before collection datetime,La date et l'heure du test de laboratoire ne peuvent pas être avant la date et l'heure de collecte, @@ -2806,7 +2806,7 @@ Stock Received But Not Billed,Stock Reçus Mais Non Facturés, Stock Reports,Rapports de stock, Stock Summary,Résumé du Stock, Stock Transactions,Transactions du Stock, -Stock UOM,UDM du Stock, +Stock UOM,UdM du Stock, Stock Value,Valeur du Stock, Stock balance in Batch {0} will become negative {1} for Item {2} at Warehouse {3},Solde du stock dans le Lot {0} deviendra négatif {1} pour l'Article {2} à l'Entrepôt {3}, Stock cannot be updated against Delivery Note {0},Stock ne peut pas être mis à jour pour le Bon de Livraison {0}, @@ -3161,9 +3161,9 @@ Trial Period End Date Cannot be before Trial Period Start Date,La date de fin de Trialling,Essai, Type of Business,Type de commerce, Types of activities for Time Logs,Types d'activités pour Journaux de Temps, -UOM,UDM, -UOM Conversion factor is required in row {0},Facteur de conversion de l'UDM est obligatoire dans la ligne {0}, -UOM coversion factor required for UOM: {0} in Item: {1},Facteur de coversion UDM requis pour l'UDM : {0} dans l'Article : {1}, +UOM,UdM, +UOM Conversion factor is required in row {0},Facteur de conversion de l'UdM est obligatoire dans la ligne {0}, +UOM coversion factor required for UOM: {0} in Item: {1},Facteur de coversion UdM requis pour l'UdM : {0} dans l'Article : {1}, URL,URL, Unable to find DocType {0},Impossible de trouver le DocType {0}, Unable to find exchange rate for {0} to {1} for key date {2}. Please create a Currency Exchange record manually,Impossible de trouver le taux de change pour {0} à {1} pour la date clé {2}. Veuillez créer une entrée de taux de change manuellement, @@ -3294,7 +3294,7 @@ Wednesday,Mercredi, Week,Semaine, Weekdays,Jours de la semaine, Weekly,Hebdomadaire, -"Weight is mentioned,\nPlease mention ""Weight UOM"" too","Poids est mentionné,\nVeuillez aussi mentionner ""UDM de Poids""", +"Weight is mentioned,\nPlease mention ""Weight UOM"" too","Poids est mentionné,\nVeuillez aussi mentionner ""UdM de Poids""", Welcome email sent,Email de bienvenue envoyé, Welcome to ERPNext,Bienvenue sur ERPNext, What do you need help with?,Avec quoi avez vous besoin d'aide ?, @@ -4938,12 +4938,15 @@ Is Cumulative,Est cumulatif, Coupon Code Based,Code de coupon basé, Discount on Other Item,Remise sur un autre article, Apply Rule On Other,Appliquer la règle sur autre, -Party Information,Informations sur la fête, +Party Information,Informations sur le tier, Quantity and Amount,Quantité et montant, Min Qty,Qté Min, Max Qty,Qté Max, -Min Amt,Min Amt, -Max Amt,Max Amt, +Min Amt,Montant Min, +Max Amt,Montant Max, +"If rate is zero them item will be treated as ""Free Item""",Si le prix est à 0 alors l'article sera traité comme article gratuit +Is Recursive,Est récursif +"Discounts to be applied in sequential ranges like buy 1 get 1, buy 2 get 2, buy 3 get 3 and so on",La remise sera appliquée séquentiellement telque : acheter 1 => recupérer 1, acheter 2 => recupérer 2, acheter 3 => recupérer 3, etc... Period Settings,Paramètres de période, Margin,Marge, Margin Type,Type de Marge, @@ -5053,7 +5056,7 @@ Quantity and Rate,Quantité et Prix, Received Qty,Qté Reçue, Accepted Qty,Quantité acceptée, Rejected Qty,Qté Rejetée, -UOM Conversion Factor,Facteur de Conversion de l'UDM, +UOM Conversion Factor,Facteur de Conversion de l'UdM, Discount on Price List Rate (%),Remise sur la Liste des Prix (%), Price List Rate (Company Currency),Taux de la Liste de Prix (Devise Société), Rate (Company Currency),Prix (Devise Société), @@ -5085,7 +5088,7 @@ Purchase Receipt Detail,Détail du reçu d'achat, Item Weight Details,Détails du poids de l'article, Weight Per Unit,Poids par unité, Total Weight,Poids total, -Weight UOM,UDM de Poids, +Weight UOM,UdM de Poids, Page Break,Saut de Page, Consider Tax or Charge for,Tenir Compte de la Taxe et des Frais pour, Valuation and Total,Valorisation et Total, @@ -5153,7 +5156,7 @@ Advance amount,Montant de l'Avance, Sales Invoice Item,Article de la Facture de Vente, Customer's Item Code,Code de l'Article du Client, Brand Name,Nom de la Marque, -Qty as per Stock UOM,Qté par UDM du Stock, +Qty as per Stock UOM,Qté par UdM du Stock, Discount and Margin,Remise et Marge, Rate With Margin,Prix Avec Marge, Discount (%) on Price List Rate with Margin,Remise (%) sur le prix de la Liste de Prix avec la Marge, @@ -5501,7 +5504,7 @@ Blanket Order Rate,Prix unitaire de commande avec limites, Returned Qty,Qté Retournée, Purchase Order Item Supplied,Article Fourni depuis la Commande d'Achat, BOM Detail No,N° de Détail de la nomenclature, -Stock Uom,UDM du Stock, +Stock Uom,UdM du Stock, Raw Material Item Code,Code d’Article de Matière Première, Supplied Qty,Qté Fournie, Purchase Receipt Item Supplied,Articles Fournis du Reçus d’Achat, @@ -6149,7 +6152,7 @@ Drug Name / Description,Nom / description du médicament, Dosage,Dosage, Dosage by Time Interval,Dosage par intervalle de temps, Interval,Intervalle, -Interval UOM,UDM d'Intervalle, +Interval UOM,UdM d'Intervalle, Hour,Heure, Update Schedule,Mettre à Jour le Calendrier, Exercise,Exercice, @@ -7023,7 +7026,7 @@ Petrol,Essence, Diesel,Diesel, Natural Gas,Gaz Naturel, Electric,Électrique, -Fuel UOM,UDM Carburant, +Fuel UOM,UdM Carburant, Last Carbon Check,Dernière Vérification Carbone, Wheels,Roues, Doors,Portes, @@ -7182,7 +7185,7 @@ Item to be manufactured or repacked,Article à produire ou à réemballer, Quantity of item obtained after manufacturing / repacking from given quantities of raw materials,Quantité d'article obtenue après production / reconditionnement des quantités données de matières premières, Set rate of sub-assembly item based on BOM,Définir le prix des articles de sous-assemblage en fonction de la nomenclature, Allow Alternative Item,Autoriser un article alternatif, -Item UOM,UDM de l'Article, +Item UOM,UdM de l'Article, Conversion Rate,Taux de Conversion, Rate Of Materials Based On,Prix des Matériaux Basé sur, With Operations,Avec des Opérations, @@ -7926,7 +7929,7 @@ Territory Manager,Responsable Régional, For reference,Pour référence, Territory Targets,Objectifs Régionaux, Set Item Group-wise budgets on this Territory. You can also include seasonality by setting the Distribution.,Définir des budgets par Groupes d'Articles sur ce Territoire. Vous pouvez également inclure de la saisonnalité en définissant la Répartition., -UOM Name,Nom UDM, +UOM Name,Nom UdM, Check this to disallow fractions. (for Nos),Cochez cette case pour interdire les fractions. (Pour les numéros), Website Item Group,Groupe d'Articles du Site Web, Cross Listing of Item in multiple groups,Liste Croisée d'Articles dans plusieurs groupes, @@ -8198,10 +8201,10 @@ To Package No.,Au N° de Paquet, If more than one package of the same type (for print),Si plus d'un paquet du même type (pour l'impression), Package Weight Details,Détails du Poids du Paquet, The net weight of this package. (calculated automatically as sum of net weight of items),Le poids net de ce paquet. (Calculé automatiquement comme la somme du poids net des articles), -Net Weight UOM,UDM Poids Net, +Net Weight UOM,UdM Poids Net, Gross Weight,Poids Brut, The gross weight of the package. Usually net weight + packaging material weight. (for print),Le poids brut du colis. Habituellement poids net + poids du matériau d'emballage. (Pour l'impression), -Gross Weight UOM,UDM du Poids Brut, +Gross Weight UOM,UdM du Poids Brut, Packing Slip Item,Article Emballé, DN Detail,Détail du Bon de Livraison, STO-PICK-.YYYY.-,STO-PICK-.YYYY.-, @@ -8215,7 +8218,7 @@ Pick List Item,Élément de la liste de choix, Picked Qty,Quantité choisie, Price List Master,Données de Base des Listes de Prix, Price List Name,Nom de la Liste de Prix, -Price Not UOM Dependent,Prix non dépendant de l'UOM, +Price Not UOM Dependent,Prix non dépendant de l'UdM, Applicable for Countries,Applicable pour les Pays, Price List Country,Pays de la Liste des Prix, MAT-PRE-.YYYY.-,MAT-PRE-YYYY.-, @@ -8294,7 +8297,7 @@ Purchase Receipt No,N° du Reçu d'Achat, Inspection Required,Inspection obligatoire, From BOM,Depuis la nomenclature, For Quantity,Pour la Quantité, -As per Stock UOM,Selon UDM du Stock, +As per Stock UOM,Selon UdM du Stock, Including items for sub assemblies,Incluant les articles pour des sous-ensembles, Default Source Warehouse,Entrepôt Source par Défaut, Source Warehouse Address,Adresse de l'entrepôt source, @@ -8309,7 +8312,7 @@ Total Additional Costs,Total des Coûts Additionnels, Customer or Supplier Details,Détails du Client ou du Fournisseur, Per Transferred,Par transféré, Stock Entry Detail,Détails de l'Écriture de Stock, -Basic Rate (as per Stock UOM),Prix de base (comme l’UDM du Stock), +Basic Rate (as per Stock UOM),Prix de base (comme l’UdM du Stock), Basic Amount,Montant de Base, Additional Cost,Frais Supplémentaire, Serial No / Batch,N° de Série / Lot, @@ -8339,7 +8342,7 @@ Quantity Difference,Différence de Quantité, Amount Difference,Différence de Montant, Item Naming By,Nomenclature d'Article Par, Default Item Group,Groupe d'Éléments par Défaut, -Default Stock UOM,UDM par Défaut des Articles, +Default Stock UOM,UdM par Défaut des Articles, Sample Retention Warehouse,Entrepôt de stockage des échantillons, Default Valuation Method,Méthode de Valorisation par Défaut, Show Barcode Field,Afficher Champ Code Barre, @@ -8353,8 +8356,8 @@ Stock Frozen Upto,Stock Gelé Jusqu'au, Batch Identification,Identification par lots, Use Naming Series,Utiliser la série de noms, Naming Series Prefix,Préfix du nom de série, -UOM Category,Catégorie d'unité de mesure (UDM), -UOM Conversion Detail,Détails de Conversion de l'UDM, +UOM Category,Catégorie d'unité de mesure (UdM), +UOM Conversion Detail,Détails de Conversion de l'UdM, Variant Field,Champ de Variante, A logical Warehouse against which stock entries are made.,Un Entrepôt logique dans lequel les entrées en stock sont faites., Warehouse Detail,Détail de l'Entrepôt, @@ -9869,8 +9872,8 @@ Allowed Items,Articles autorisés Party Specific Item,Restriction d'article disponible Restrict Items Based On,Type de critére de restriction Based On Value,critére de restriction -Unit of Measure (UOM),Unité de mesure (UDM), -Unit Of Measure (UOM),Unité de mesure (UDM), +Unit of Measure (UOM),Unité de mesure (UdM), +Unit Of Measure (UOM),Unité de mesure (UdM), CRM Settings,Paramètres CRM Do Not Explode,Ne pas décomposer Quick Access, Accés rapides From 8d1db0ea3db008d753048ca53356846af44de775 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 6 Oct 2022 11:28:26 +0530 Subject: [PATCH 0302/1047] fix: single column indexes (#32425) refactor: move single column indexes to doctypes --- erpnext/accounts/doctype/pos_invoice/pos_invoice.json | 5 +++-- erpnext/accounts/doctype/pos_invoice/pos_invoice.py | 4 ---- erpnext/e_commerce/doctype/website_item/website_item.json | 8 +++++--- erpnext/e_commerce/doctype/website_item/website_item.py | 3 --- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index 6f8b3822c2..eedaaaf338 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -343,7 +343,8 @@ "no_copy": 1, "options": "POS Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "default": "0", @@ -1553,7 +1554,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2022-09-27 13:00:24.166684", + "modified": "2022-09-30 03:49:50.455199", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index fbe0ef39f8..54a3e934b2 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -743,7 +743,3 @@ def add_return_modes(doc, pos_profile): ]: payment_mode = get_mode_of_payment_info(mode_of_payment, doc.company) append_payment(payment_mode[0]) - - -def on_doctype_update(): - frappe.db.add_index("POS Invoice", ["return_against"]) diff --git a/erpnext/e_commerce/doctype/website_item/website_item.json b/erpnext/e_commerce/doctype/website_item/website_item.json index c5775ee907..6556eabf4a 100644 --- a/erpnext/e_commerce/doctype/website_item/website_item.json +++ b/erpnext/e_commerce/doctype/website_item/website_item.json @@ -188,7 +188,8 @@ "in_list_view": 1, "label": "Item Group", "options": "Item Group", - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "default": "1", @@ -234,7 +235,8 @@ "fieldname": "brand", "fieldtype": "Link", "label": "Brand", - "options": "Brand" + "options": "Brand", + "search_index": 1 }, { "collapsible": 1, @@ -346,7 +348,7 @@ "index_web_pages_for_search": 1, "links": [], "make_attachments_public": 1, - "modified": "2022-09-13 04:05:11.614087", + "modified": "2022-09-30 04:01:52.090732", "modified_by": "Administrator", "module": "E-commerce", "name": "Website Item", diff --git a/erpnext/e_commerce/doctype/website_item/website_item.py b/erpnext/e_commerce/doctype/website_item/website_item.py index c0f8c79283..3e5d5f768f 100644 --- a/erpnext/e_commerce/doctype/website_item/website_item.py +++ b/erpnext/e_commerce/doctype/website_item/website_item.py @@ -403,9 +403,6 @@ def on_doctype_update(): # since route is a Text column, it needs a length for indexing frappe.db.add_index("Website Item", ["route(500)"]) - frappe.db.add_index("Website Item", ["item_group"]) - frappe.db.add_index("Website Item", ["brand"]) - def check_if_user_is_customer(user=None): from frappe.contacts.doctype.contact.contact import get_contact_name From 07c4a7483869a06c1e00a2a71c1909fcf5cd9ed6 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 6 Oct 2022 13:01:53 +0530 Subject: [PATCH 0303/1047] chore: drop stale demo page (#32515) --- erpnext/templates/pages/demo.html | 77 ------------------------------- 1 file changed, 77 deletions(-) delete mode 100644 erpnext/templates/pages/demo.html diff --git a/erpnext/templates/pages/demo.html b/erpnext/templates/pages/demo.html deleted file mode 100644 index f9934a33f3..0000000000 --- a/erpnext/templates/pages/demo.html +++ /dev/null @@ -1,77 +0,0 @@ -{% extends "templates/web.html" %} - -{% block script %} - -{% endblock %} - -{% block style %} - -{% endblock %} - -{% block title %} -{{ _("ERPNext Demo") }} -{% endblock %} - -{% block page_content %} -
- -
- - {{ _("ERPNext Demo") }} -
- -

Some functionality is disabled for the demo and the data will be cleared regularly.

-
-
- - -

Start a free 14-day trial -

- -{% endblock %} From c18f13a45ba6498245ba0429e61205a0df2434a6 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 6 Oct 2022 13:35:11 +0530 Subject: [PATCH 0304/1047] refactor: rewrite `Stock Projected Qty Report` queries in `QB` --- .../stock_projected_qty.py | 106 ++++++++++-------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py index 49e797d6a3..f477d8f08f 100644 --- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py +++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py @@ -5,6 +5,7 @@ import frappe from frappe import _ from frappe.utils import flt, today +from pypika.terms import ExistsCriterion from erpnext.accounts.doctype.pos_invoice.pos_invoice import get_pos_reserved_qty from erpnext.stock.utils import ( @@ -218,10 +219,26 @@ def get_columns(): def get_bin_list(filters): - conditions = [] + bin = frappe.qb.DocType("Bin") + query = ( + frappe.qb.from_(bin) + .select( + bin.item_code, + bin.warehouse, + bin.actual_qty, + bin.planned_qty, + bin.indented_qty, + bin.ordered_qty, + bin.reserved_qty, + bin.reserved_qty_for_production, + bin.reserved_qty_for_sub_contract, + bin.projected_qty, + ) + .orderby(bin.item_code, bin.warehouse) + ) if filters.item_code: - conditions.append("item_code = '%s' " % filters.item_code) + query = query.where(bin.item_code == filters.item_code) if filters.warehouse: warehouse_details = frappe.db.get_value( @@ -229,21 +246,20 @@ def get_bin_list(filters): ) if warehouse_details: - conditions.append( - " exists (select name from `tabWarehouse` wh \ - where wh.lft >= %s and wh.rgt <= %s and bin.warehouse = wh.name)" - % (warehouse_details.lft, warehouse_details.rgt) + wh = frappe.qb.DocType("Warehouse") + query = query.where( + ExistsCriterion( + frappe.qb.from_(wh) + .select(wh.name) + .where( + (wh.lft >= warehouse_details.lft) + & (wh.rgt <= warehouse_details.rgt) + & (bin.warehouse == wh.name) + ) + ) ) - bin_list = frappe.db.sql( - """select item_code, warehouse, actual_qty, planned_qty, indented_qty, - ordered_qty, reserved_qty, reserved_qty_for_production, reserved_qty_for_sub_contract, projected_qty - from tabBin bin {conditions} order by item_code, warehouse - """.format( - conditions=" where " + " and ".join(conditions) if conditions else "" - ), - as_dict=1, - ) + bin_list = query.run(as_dict=True) return bin_list @@ -251,45 +267,43 @@ def get_bin_list(filters): def get_item_map(item_code, include_uom): """Optimization: get only the item doc and re_order_levels table""" - condition = "" - if item_code: - condition = "and item_code = {0}".format(frappe.db.escape(item_code, percent=False)) + bin = frappe.qb.DocType("Bin") + item = frappe.qb.DocType("Item") - cf_field = cf_join = "" - if include_uom: - cf_field = ", ucd.conversion_factor" - cf_join = ( - "left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom=%(include_uom)s" + query = ( + frappe.qb.from_(item) + .select(item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom) + .where( + (item.is_stock_item == 1) + & (item.disabled == 0) + & ( + (item.end_of_life > today()) | (item.end_of_life.isnull()) | (item.end_of_life == "0000-00-00") + ) + & (ExistsCriterion(frappe.qb.from_(bin).select(bin.name).where(bin.item_code == item.name))) ) - - items = frappe.db.sql( - """ - select item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom{cf_field} - from `tabItem` item - {cf_join} - where item.is_stock_item = 1 - and item.disabled=0 - {condition} - and (item.end_of_life > %(today)s or item.end_of_life is null or item.end_of_life='0000-00-00') - and exists (select name from `tabBin` bin where bin.item_code=item.name)""".format( - cf_field=cf_field, cf_join=cf_join, condition=condition - ), - {"today": today(), "include_uom": include_uom}, - as_dict=True, ) - condition = "" if item_code: - condition = "where parent={0}".format(frappe.db.escape(item_code, percent=False)) + query = query.where(item.item_code == item_code) + + if include_uom: + ucd = frappe.qb.DocType("UOM Conversion Detail") + query = query.left_join(ucd).on((ucd.parent == item.name) & (ucd.uom == include_uom)) + + items = query.run(as_dict=True) + + ir = frappe.qb.DocType("Item Reorder") + query = frappe.qb.from_(ir).select("*") + + if item_code: + query = query.where(ir.parent == item_code) reorder_levels = frappe._dict() - for ir in frappe.db.sql( - """select * from `tabItem Reorder` {condition}""".format(condition=condition), as_dict=1 - ): - if ir.parent not in reorder_levels: - reorder_levels[ir.parent] = [] + for d in query.run(as_dict=True): + if d.parent not in reorder_levels: + reorder_levels[d.parent] = [] - reorder_levels[ir.parent].append(ir) + reorder_levels[d.parent].append(d) item_map = frappe._dict() for item in items: From d3c073dc25c6b86abef3f96560a5ed19d4c2f42a Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 6 Oct 2022 17:19:29 +0530 Subject: [PATCH 0305/1047] refactor: rewrite `Supplier-Wise Sales Analytics Report` queries in `QB` --- .../supplier_wise_sales_analytics.py | 117 ++++++++++++------ 1 file changed, 77 insertions(+), 40 deletions(-) diff --git a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py index 5430fe6969..8c76908cc3 100644 --- a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py +++ b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py @@ -4,6 +4,7 @@ import frappe from frappe import _ +from frappe.query_builder.functions import IfNull from frappe.utils import flt @@ -70,31 +71,33 @@ def get_columns(filters): return columns -def get_conditions(filters): - conditions = "" - values = [] +def get_consumed_details(filters): + item = frappe.qb.DocType("Item") + sle = frappe.qb.DocType("Stock Ledger Entry") + + query = ( + frappe.qb.from_(sle) + .from_(item) + .select( + sle.item_code, + item.item_name, + item.description, + item.stock_uom, + sle.actual_qty, + sle.stock_value_difference, + sle.voucher_no, + sle.voucher_type, + ) + .where((sle.is_cancelled == 0) & (sle.item_code == item.name) & (sle.actual_qty < 0)) + ) if filters.get("from_date") and filters.get("to_date"): - conditions = "and sle.posting_date>=%s and sle.posting_date<=%s" - values = [filters.get("from_date"), filters.get("to_date")] + query = query.where( + (sle.posting_date >= filters.get("from_date")) & (sle.posting_date <= filters.get("to_date")) + ) - return conditions, values - - -def get_consumed_details(filters): - conditions, values = get_conditions(filters) consumed_details = {} - - for d in frappe.db.sql( - """select sle.item_code, i.item_name, i.description, - i.stock_uom, sle.actual_qty, sle.stock_value_difference, - sle.voucher_no, sle.voucher_type - from `tabStock Ledger Entry` sle, `tabItem` i - where sle.is_cancelled = 0 and sle.item_code=i.name and sle.actual_qty < 0 %s""" - % conditions, - values, - as_dict=1, - ): + for d in query.run(as_dict=True): consumed_details.setdefault(d.item_code, []).append(d) return consumed_details @@ -104,24 +107,54 @@ def get_suppliers_details(filters): item_supplier_map = {} supplier = filters.get("supplier") - for d in frappe.db.sql( - """select pr.supplier, pri.item_code from - `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri - where pr.name=pri.parent and pr.docstatus=1 and - pri.item_code=(select name from `tabItem` where - is_stock_item=1 and name=pri.item_code)""", - as_dict=1, - ): + item = frappe.qb.DocType("Item") + pr = frappe.qb.DocType("Purchase Receipt") + pr_item = frappe.qb.DocType("Purchase Receipt Item") + + query = ( + frappe.qb.from_(pr) + .from_(pr_item) + .select(pr.supplier, pr_item.item_code) + .where( + (pr.name == pr_item.parent) + & (pr.docstatus == 1) + & ( + pr_item.item_code + == ( + frappe.qb.from_(item) + .select(item.name) + .where((item.is_stock_item == 1) & (item.name == pr_item.item_code)) + ) + ) + ) + ) + + for d in query.run(as_dict=True): item_supplier_map.setdefault(d.item_code, []).append(d.supplier) - for d in frappe.db.sql( - """select pr.supplier, pri.item_code from - `tabPurchase Invoice` pr, `tabPurchase Invoice Item` pri - where pr.name=pri.parent and pr.docstatus=1 and - ifnull(pr.update_stock, 0) = 1 and pri.item_code=(select name from `tabItem` - where is_stock_item=1 and name=pri.item_code)""", - as_dict=1, - ): + pi = frappe.qb.DocType("Purchase Invoice") + pi_item = frappe.qb.DocType("Purchase Invoice Item") + + query = ( + frappe.qb.from_(pi) + .from_(pi_item) + .select(pi.supplier, pi_item.item_code) + .where( + (pi.name == pi_item.parent) + & (pi.docstatus == 1) + & (IfNull(pi.update_stock, 0) == 1) + & ( + pi_item.item_code + == ( + frappe.qb.from_(item) + .select(item.name) + .where((item.is_stock_item == 1) & (item.name == pi_item.item_code)) + ) + ) + ) + ) + + for d in query.run(as_dict=True): if d.item_code not in item_supplier_map: item_supplier_map.setdefault(d.item_code, []).append(d.supplier) @@ -138,7 +171,11 @@ def get_suppliers_details(filters): def get_material_transfer_vouchers(): - return frappe.db.sql_list( - """select name from `tabStock Entry` where - purpose='Material Transfer' and docstatus=1""" + se = frappe.qb.DocType("Stock Entry") + query = ( + frappe.qb.from_(se) + .select(se.name) + .where((se.purpose == "Material Transfer") & (se.docstatus == 1)) ) + + return [r[0] for r in query.run()] From 8376fbc9826e3fa7dc9a28365d96e92a2f6f52d9 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 6 Oct 2022 20:35:33 +0530 Subject: [PATCH 0306/1047] fix: Explicitly update modified (#32519) * fix: Explicitly update modified required after https://github.com/frappe/frappe/pull/18301 * chore: fix broken translations --- erpnext/stock/doctype/bin/bin.py | 11 +++++++---- erpnext/stock/stock_ledger.py | 2 +- erpnext/translations/fr.csv | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 548df318fa..c28f45aed4 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -37,8 +37,10 @@ class Bin(Document): self.set_projected_qty() - self.db_set("reserved_qty_for_production", flt(self.reserved_qty_for_production)) - self.db_set("projected_qty", self.projected_qty) + self.db_set( + "reserved_qty_for_production", flt(self.reserved_qty_for_production), update_modified=True + ) + self.db_set("projected_qty", self.projected_qty, update_modified=True) def update_reserved_qty_for_sub_contracting(self, subcontract_doctype="Subcontracting Order"): # reserved qty @@ -118,9 +120,9 @@ class Bin(Document): else: reserved_qty_for_sub_contract = 0 - self.db_set("reserved_qty_for_sub_contract", reserved_qty_for_sub_contract) + self.db_set("reserved_qty_for_sub_contract", reserved_qty_for_sub_contract, update_modified=True) self.set_projected_qty() - self.db_set("projected_qty", self.projected_qty) + self.db_set("projected_qty", self.projected_qty, update_modified=True) def on_doctype_update(): @@ -193,4 +195,5 @@ def update_qty(bin_name, args): "planned_qty": planned_qty, "projected_qty": projected_qty, }, + update_modified=True, ) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 50309647de..9ca40c3675 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1053,7 +1053,7 @@ class update_entries_after(object): updated_values = {"actual_qty": data.qty_after_transaction, "stock_value": data.stock_value} if data.valuation_rate is not None: updated_values["valuation_rate"] = data.valuation_rate - frappe.db.set_value("Bin", bin_name, updated_values) + frappe.db.set_value("Bin", bin_name, updated_values, update_modified=True) def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False): diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv index 7989bf7ebc..3ba5ade629 100644 --- a/erpnext/translations/fr.csv +++ b/erpnext/translations/fr.csv @@ -4946,7 +4946,7 @@ Min Amt,Montant Min, Max Amt,Montant Max, "If rate is zero them item will be treated as ""Free Item""",Si le prix est à 0 alors l'article sera traité comme article gratuit Is Recursive,Est récursif -"Discounts to be applied in sequential ranges like buy 1 get 1, buy 2 get 2, buy 3 get 3 and so on",La remise sera appliquée séquentiellement telque : acheter 1 => recupérer 1, acheter 2 => recupérer 2, acheter 3 => recupérer 3, etc... +"Discounts to be applied in sequential ranges like buy 1 get 1, buy 2 get 2, buy 3 get 3 and so on","La remise sera appliquée séquentiellement telque : acheter 1 => recupérer 1, acheter 2 => recupérer 2, acheter 3 => recupérer 3, etc..." Period Settings,Paramètres de période, Margin,Marge, Margin Type,Type de Marge, From d7c3b7633a4951102c36dffb811f364bb3b45aae Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 6 Oct 2022 22:36:29 +0530 Subject: [PATCH 0307/1047] fix: make readings status mandatory in Quality Inspection --- .../doctype/quality_inspection/quality_inspection.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index 13abfad455..d082000434 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -30,6 +30,9 @@ class QualityInspection(Document): if self.readings: self.inspect_and_set_status() + def before_submit(self): + self.validate_readings_status_mandatory() + @frappe.whitelist() def get_item_specification_details(self): if not self.quality_inspection_template: @@ -65,6 +68,11 @@ class QualityInspection(Document): def on_cancel(self): self.update_qc_reference() + def validate_readings_status_mandatory(self): + for reading in self.readings: + if not reading.status: + frappe.throw(_("Row #{0}: Status is mandatory").format(reading.idx)) + def update_qc_reference(self): quality_inspection = self.name if self.docstatus == 1 else "" From 7c759b193ca957d039165209c9e9237bf93fd5e6 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 7 Oct 2022 09:59:28 +0530 Subject: [PATCH 0308/1047] refactor: rewrite `Total Stock Summary Report` queries in `QB` --- .../total_stock_summary.py | 58 +++++++++---------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/erpnext/stock/report/total_stock_summary/total_stock_summary.py b/erpnext/stock/report/total_stock_summary/total_stock_summary.py index 21529da2a1..c3155bd1a5 100644 --- a/erpnext/stock/report/total_stock_summary/total_stock_summary.py +++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.py @@ -4,60 +4,58 @@ import frappe from frappe import _ +from frappe.query_builder.functions import Sum def execute(filters=None): if not filters: filters = {} - columns = get_columns() + columns = get_columns(filters) stock = get_total_stock(filters) return columns, stock -def get_columns(): +def get_columns(filters): columns = [ - _("Company") + ":Link/Company:250", - _("Warehouse") + ":Link/Warehouse:150", _("Item") + ":Link/Item:150", _("Description") + "::300", _("Current Qty") + ":Float:100", ] + if filters.get("group_by") == "Warehouse": + columns.insert(0, _("Warehouse") + ":Link/Warehouse:150") + else: + columns.insert(0, _("Company") + ":Link/Company:250") + return columns def get_total_stock(filters): - conditions = "" - columns = "" + bin = frappe.qb.DocType("Bin") + item = frappe.qb.DocType("Item") + wh = frappe.qb.DocType("Warehouse") + + query = ( + frappe.qb.from_(bin) + .inner_join(item) + .on(bin.item_code == item.item_code) + .inner_join(wh) + .on(wh.name == bin.warehouse) + .where(bin.actual_qty != 0) + ) if filters.get("group_by") == "Warehouse": if filters.get("company"): - conditions += " AND warehouse.company = %s" % frappe.db.escape( - filters.get("company"), percent=False - ) + query = query.where(wh.company == filters.get("company")) - conditions += " GROUP BY ledger.warehouse, item.item_code" - columns += "'' as company, ledger.warehouse" + query = query.select(bin.warehouse).groupby(bin.warehouse) else: - conditions += " GROUP BY warehouse.company, item.item_code" - columns += " warehouse.company, '' as warehouse" + query = query.select(wh.company).groupby(wh.company) - return frappe.db.sql( - """ - SELECT - %s, - item.item_code, - item.description, - sum(ledger.actual_qty) as actual_qty - FROM - `tabBin` AS ledger - INNER JOIN `tabItem` AS item - ON ledger.item_code = item.item_code - INNER JOIN `tabWarehouse` warehouse - ON warehouse.name = ledger.warehouse - WHERE - ledger.actual_qty != 0 %s""" - % (columns, conditions) - ) + query = query.select( + item.item_code, item.description, Sum(bin.actual_qty).as_("actual_qty") + ).groupby(item.item_code) + + return query.run() From 8103856a41279ac3278d560a99f603604b0fd956 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 6 Oct 2022 20:58:00 +0530 Subject: [PATCH 0309/1047] refactor: rewrite `Warehouse wise Item Balance Age and Value Report` queries in `QB` --- ...rehouse_wise_item_balance_age_and_value.py | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py index a54373f364..eedf1a0460 100644 --- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py +++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py @@ -7,6 +7,7 @@ import frappe from frappe import _ +from frappe.query_builder.functions import Count from frappe.utils import flt from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, get_average_age @@ -98,7 +99,7 @@ def get_columns(filters): def validate_filters(filters): if not (filters.get("item_code") or filters.get("warehouse")): - sle_count = flt(frappe.db.sql("""select count(name) from `tabStock Ledger Entry`""")[0][0]) + sle_count = flt(frappe.qb.from_("Stock Ledger Entry").select(Count("name")).run()[0][0]) if sle_count > 500000: frappe.throw(_("Please set filter based on Item or Warehouse")) if not filters.get("company"): @@ -108,25 +109,16 @@ def validate_filters(filters): def get_warehouse_list(filters): from frappe.core.doctype.user_permission.user_permission import get_permitted_documents - condition = "" - user_permitted_warehouse = get_permitted_documents("Warehouse") - value = () - if user_permitted_warehouse: - condition = "and name in %s" - value = set(user_permitted_warehouse) - elif not user_permitted_warehouse and filters.get("warehouse"): - condition = "and name = %s" - value = filters.get("warehouse") + wh = frappe.qb.DocType("Warehouse") + query = frappe.qb.from_(wh).select(wh.name).where(wh.is_group == 0) - return frappe.db.sql( - """select name - from `tabWarehouse` where is_group = 0 - {condition}""".format( - condition=condition - ), - value, - as_dict=1, - ) + user_permitted_warehouse = get_permitted_documents("Warehouse") + if user_permitted_warehouse: + query = query.where(wh.name.isin(set(user_permitted_warehouse))) + elif filters.get("warehouse"): + query = query.where(wh.name == filters.get("warehouse")) + + return query.run(as_dict=True) def add_warehouse_column(columns, warehouse_list): From abf5b6be3ea13f0d00664e25e7b1742429d4f5e2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 7 Oct 2022 14:04:36 +0530 Subject: [PATCH 0310/1047] fix: Tax withholding related fixes --- .../tax_withholding_category/tax_withholding_category.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 84c2c9a3c3..61f52a18aa 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -425,7 +425,10 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers): ): # Get net total again as TDS is calculated on net total # Grand is used to just check for threshold breach - net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)") or 0.0 + net_total = 0 + if vouchers: + net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)") + net_total += inv.net_total supp_credit_amt = net_total - cumulative_threshold From 781d160c684f9c04b1c799449e484abff3e07d8a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 7 Oct 2022 14:22:40 +0530 Subject: [PATCH 0311/1047] fix: Do not add tax withheld vouchers post tax withheding in one document --- .../accounts/doctype/purchase_invoice/purchase_invoice.json | 3 ++- .../tax_withholding_category/tax_withholding_category.py | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 3020e6dc6e..1e477776e2 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1430,6 +1430,7 @@ "fieldname": "tax_withheld_vouchers", "fieldtype": "Table", "label": "Tax Withheld Vouchers", + "no_copy": 1, "options": "Tax Withheld Vouchers", "read_only": 1 } @@ -1438,7 +1439,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2022-09-27 11:07:55.766844", + "modified": "2022-10-07 14:19:14.214157", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", 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 61f52a18aa..7eddd81ee0 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -249,6 +249,9 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N ) else: tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0 + + # once tds is deducted, not need to add vouchers in the invoice + voucher_wise_amount = {} else: tax_amount = get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers) From a5b3f8cae9b028a9614f66cab6ba43ffc260b365 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 7 Oct 2022 17:23:33 +0530 Subject: [PATCH 0312/1047] refactor: rewrite `Purchase Order Analysis Report` queries in `QB` --- .../purchase_order_analysis.py | 100 +++++++++--------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index a5c464910d..e10c0e2fcc 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -6,6 +6,7 @@ import copy import frappe from frappe import _ +from frappe.query_builder.functions import IfNull from frappe.utils import date_diff, flt, getdate @@ -16,9 +17,7 @@ def execute(filters=None): validate_filters(filters) columns = get_columns(filters) - conditions = get_conditions(filters) - - data = get_data(conditions, filters) + data = get_data(filters) if not data: return [], [], None, [] @@ -37,60 +36,61 @@ def validate_filters(filters): frappe.throw(_("To Date cannot be before From Date.")) -def get_conditions(filters): - conditions = "" - if filters.get("from_date") and filters.get("to_date"): - conditions += " and po.transaction_date between %(from_date)s and %(to_date)s" +def get_data(filters): + po = frappe.qb.DocType("Purchase Order") + po_item = frappe.qb.DocType("Purchase Order Item") + pi_item = frappe.qb.DocType("Purchase Invoice Item") - for field in ["company", "name"]: + query = ( + frappe.qb.from_(po) + .from_(po_item) + .left_join(pi_item) + .on(pi_item.po_detail == po_item.name) + .select( + po.transaction_date.as_("date"), + po_item.schedule_date.as_("required_date"), + po_item.project, + po.name.as_("purchase_order"), + po.status, + po.supplier, + po_item.item_code, + po_item.qty, + po_item.received_qty, + (po_item.qty - po_item.received_qty).as_("pending_qty"), + IfNull(pi_item.qty, 0).as_("billed_qty"), + po_item.base_amount.as_("amount"), + (po_item.received_qty * po_item.base_rate).as_("received_qty_amount"), + (po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"), + (po_item.base_amount - (po_item.billed_amt * IfNull(po.conversion_rate, 1))).as_( + "pending_amount" + ), + po.set_warehouse.as_("warehouse"), + po.company, + po_item.name, + ) + .where( + (po_item.parent == po.name) & (po.status.notin(("Stopped", "Closed"))) & (po.docstatus == 1) + ) + .groupby(po_item.name) + .orderby(po.transaction_date) + ) + + for field in ("company", "name"): if filters.get(field): - conditions += f" and po.{field} = %({field})s" + query = query.where(po[field] == filters.get(field)) + + if filters.get("from_date") and filters.get("to_date"): + query = query.where( + po.transaction_date.between(filters.get("from_date"), filters.get("to_date")) + ) if filters.get("status"): - conditions += " and po.status in %(status)s" + query = query.where(po.status.isin(filters.get("status"))) if filters.get("project"): - conditions += " and poi.project = %(project)s" + query = query.where(po_item.project == filters.get("project")) - return conditions - - -def get_data(conditions, filters): - data = frappe.db.sql( - """ - SELECT - po.transaction_date as date, - poi.schedule_date as required_date, - poi.project, - po.name as purchase_order, - po.status, po.supplier, poi.item_code, - poi.qty, poi.received_qty, - (poi.qty - poi.received_qty) AS pending_qty, - IFNULL(pii.qty, 0) as billed_qty, - poi.base_amount as amount, - (poi.received_qty * poi.base_rate) as received_qty_amount, - (poi.billed_amt * IFNULL(po.conversion_rate, 1)) as billed_amount, - (poi.base_amount - (poi.billed_amt * IFNULL(po.conversion_rate, 1))) as pending_amount, - po.set_warehouse as warehouse, - po.company, poi.name - FROM - `tabPurchase Order` po, - `tabPurchase Order Item` poi - LEFT JOIN `tabPurchase Invoice Item` pii - ON pii.po_detail = poi.name - WHERE - poi.parent = po.name - and po.status not in ('Stopped', 'Closed') - and po.docstatus = 1 - {0} - GROUP BY poi.name - ORDER BY po.transaction_date ASC - """.format( - conditions - ), - filters, - as_dict=1, - ) + data = query.run(as_dict=True) return data From a14b9c7baceb45f0cff43c9b5717896d37d13137 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 7 Oct 2022 18:01:09 +0530 Subject: [PATCH 0313/1047] refactor: rewrite `Supplier Quotation Comparison Report` queries in `QB` --- .../supplier_quotation_comparison.py | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py index 3013b6d160..a728290961 100644 --- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py +++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py @@ -16,8 +16,7 @@ def execute(filters=None): return [], [] columns = get_columns(filters) - conditions = get_conditions(filters) - supplier_quotation_data = get_data(filters, conditions) + supplier_quotation_data = get_data(filters) data, chart_data = prepare_data(supplier_quotation_data, filters) message = get_message() @@ -25,50 +24,51 @@ def execute(filters=None): return columns, data, message, chart_data -def get_conditions(filters): - conditions = "" +def get_data(filters): + sq = frappe.qb.DocType("Supplier Quotation") + sq_item = frappe.qb.DocType("Supplier Quotation Item") + + query = ( + frappe.qb.from_(sq_item) + .from_(sq) + .select( + sq_item.parent, + sq_item.item_code, + sq_item.qty, + sq_item.stock_qty, + sq_item.amount, + sq_item.uom, + sq_item.stock_uom, + sq_item.request_for_quotation, + sq_item.lead_time_days, + sq.supplier.as_("supplier_name"), + sq.valid_till, + ) + .where( + (sq_item.parent == sq.name) + & (sq_item.docstatus < 2) + & (sq.company == filters.get("company")) + & (sq.transaction_date.between(filters.get("from_date"), filters.get("to_date"))) + ) + .orderby(sq.transaction_date, sq_item.item_code) + ) + if filters.get("item_code"): - conditions += " AND sqi.item_code = %(item_code)s" + query = query.where(sq_item.item_code == filters.get("item_code")) if filters.get("supplier_quotation"): - conditions += " AND sqi.parent in %(supplier_quotation)s" + query = query.where(sq_item.parent.isin(filters.get("supplier_quotation"))) if filters.get("request_for_quotation"): - conditions += " AND sqi.request_for_quotation = %(request_for_quotation)s" + query = query.where(sq_item.request_for_quotation == filters.get("request_for_quotation")) if filters.get("supplier"): - conditions += " AND sq.supplier in %(supplier)s" + query = query.where(sq.supplier.isin(filters.get("supplier"))) if not filters.get("include_expired"): - conditions += " AND sq.status != 'Expired'" + query = query.where(sq.status != "Expired") - return conditions - - -def get_data(filters, conditions): - supplier_quotation_data = frappe.db.sql( - """ - SELECT - sqi.parent, sqi.item_code, - sqi.qty, sqi.stock_qty, sqi.amount, - sqi.uom, sqi.stock_uom, - sqi.request_for_quotation, - sqi.lead_time_days, sq.supplier as supplier_name, sq.valid_till - FROM - `tabSupplier Quotation Item` sqi, - `tabSupplier Quotation` sq - WHERE - sqi.parent = sq.name - AND sqi.docstatus < 2 - AND sq.company = %(company)s - AND sq.transaction_date between %(from_date)s and %(to_date)s - {0} - order by sq.transaction_date, sqi.item_code""".format( - conditions - ), - filters, - as_dict=1, - ) + supplier_quotation_data = query.run(as_dict=True) return supplier_quotation_data From d59ed24e6ca2a1ff62963c282882a2d52691b7c6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 28 Sep 2022 12:53:18 +0530 Subject: [PATCH 0314/1047] feat: provision to return non consumed components against the work order --- .../doctype/work_order/test_work_order.py | 72 ++++++++++++++ .../doctype/work_order/work_order.js | 36 ++++++- .../doctype/work_order/work_order.py | 93 ++++++++++++++++--- .../work_order_item/work_order_item.json | 13 ++- .../work_order_consumed_materials.js | 2 +- .../work_order_consumed_materials.py | 38 +++++++- .../doctype/stock_entry/stock_entry.json | 25 ++--- .../stock/doctype/stock_entry/stock_entry.py | 26 ++++-- .../doctype/stock_entry/stock_entry_list.js | 9 +- 9 files changed, 274 insertions(+), 40 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index a53c42c5ec..804f03dc51 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -17,6 +17,7 @@ from erpnext.manufacturing.doctype.work_order.work_order import ( close_work_order, make_job_card, make_stock_entry, + make_stock_return_entry, stop_unstop, ) from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order @@ -1408,6 +1409,77 @@ class TestWorkOrder(FrappeTestCase): ) self.assertEqual(manufacture_ste_doc2.items[1].qty, 1) + def test_non_consumed_material_return_against_work_order(self): + frappe.db.set_value( + "Manufacturing Settings", + None, + "backflush_raw_materials_based_on", + "Material Transferred for Manufacture", + ) + + item = make_item( + "Test FG Item To Test Return Case", + { + "is_stock_item": 1, + }, + ) + + item_code = item.name + bom_doc = make_bom( + item=item_code, + source_warehouse="Stores - _TC", + raw_materials=["Test Batch MCC Keyboard", "Test Serial No BTT Headphone"], + ) + + # Create a work order + wo_doc = make_wo_order_test_record(production_item=item_code, qty=5) + wo_doc.save() + + self.assertEqual(wo_doc.bom_no, bom_doc.name) + + # Transfer material for manufacture + ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Material Transfer for Manufacture", 5)) + for row in ste_doc.items: + row.qty += 2 + row.transfer_qty += 2 + nste_doc = test_stock_entry.make_stock_entry( + item_code=row.item_code, target="Stores - _TC", qty=row.qty, basic_rate=100 + ) + + row.batch_no = nste_doc.items[0].batch_no + row.serial_no = nste_doc.items[0].serial_no + + ste_doc.save() + ste_doc.submit() + ste_doc.load_from_db() + + # Create a stock entry to manufacture the item + ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 5)) + for row in ste_doc.items: + if row.s_warehouse and not row.t_warehouse: + row.qty -= 2 + row.transfer_qty -= 2 + + if row.serial_no: + serial_nos = get_serial_nos(row.serial_no) + row.serial_no = "\n".join(serial_nos[0:5]) + + ste_doc.save() + ste_doc.submit() + + wo_doc.load_from_db() + for row in wo_doc.required_items: + self.assertEqual(row.transferred_qty, 7) + self.assertEqual(row.consumed_qty, 5) + + self.assertEqual(wo_doc.status, "Completed") + return_ste_doc = make_stock_return_entry(wo_doc.name) + return_ste_doc.save() + + self.assertTrue(return_ste_doc.is_return) + for row in return_ste_doc.items: + self.assertEqual(row.qty, 2) + def prepare_data_for_backflush_based_on_materials_transferred(): batch_item_doc = make_item( diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index f3640b93b2..4aab3fa373 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -180,6 +180,37 @@ frappe.ui.form.on("Work Order", { frm.trigger("make_bom"); }); } + + frm.trigger("add_custom_button_to_return_components"); + }, + + add_custom_button_to_return_components: function(frm) { + if (frm.doc.docstatus === 1 && in_list(["Closed", "Completed"], frm.doc.status)) { + let non_consumed_items = frm.doc.required_items.filter(d =>{ + return flt(d.consumed_qty) < flt(d.transferred_qty - d.returned_qty) + }); + + if (non_consumed_items && non_consumed_items.length) { + frm.add_custom_button(__("Return Components"), function() { + frm.trigger("create_stock_return_entry"); + }).addClass("btn-primary"); + } + } + }, + + create_stock_return_entry: function(frm) { + frappe.call({ + method: "erpnext.manufacturing.doctype.work_order.work_order.make_stock_return_entry", + args: { + "work_order": frm.doc.name, + }, + callback: function(r) { + if(!r.exc) { + let doc = frappe.model.sync(r.message); + frappe.set_route("Form", doc[0].doctype, doc[0].name); + } + } + }); }, make_job_card: function(frm) { @@ -517,7 +548,8 @@ frappe.ui.form.on("Work Order Operation", { erpnext.work_order = { set_custom_buttons: function(frm) { var doc = frm.doc; - if (doc.docstatus === 1 && doc.status != "Closed") { + + if (doc.status !== "Closed") { frm.add_custom_button(__('Close'), function() { frappe.confirm(__("Once the Work Order is Closed. It can't be resumed."), () => { @@ -525,7 +557,9 @@ erpnext.work_order = { } ); }, __("Status")); + } + if (doc.docstatus === 1 && !in_list(["Closed", "Completed"], doc.status)) { if (doc.status != 'Stopped' && doc.status != 'Completed') { frm.add_custom_button(__('Stop'), function() { erpnext.work_order.change_work_order_status(frm, "Stopped"); diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 7b8625372a..1e6d982fc9 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -20,6 +20,7 @@ from frappe.utils import ( nowdate, time_diff_in_hours, ) +from pypika import functions as fn from erpnext.manufacturing.doctype.bom.bom import ( get_bom_item_rate, @@ -859,6 +860,7 @@ class WorkOrder(Document): if self.docstatus == 1: # calculate transferred qty based on submitted stock entries self.update_transferred_qty_for_required_items() + self.update_returned_qty() # update in bin self.update_reserved_qty_for_production() @@ -930,23 +932,62 @@ class WorkOrder(Document): self.set_available_qty() def update_transferred_qty_for_required_items(self): - """update transferred qty from submitted stock entries for that item against - the work order""" + ste = frappe.qb.DocType("Stock Entry") + ste_child = frappe.qb.DocType("Stock Entry Detail") - for d in self.required_items: - transferred_qty = frappe.db.sql( - """select sum(qty) - from `tabStock Entry` entry, `tabStock Entry Detail` detail - where - entry.work_order = %(name)s - and entry.purpose = 'Material Transfer for Manufacture' - and entry.docstatus = 1 - and detail.parent = entry.name - and (detail.item_code = %(item)s or detail.original_item = %(item)s)""", - {"name": self.name, "item": d.item_code}, - )[0][0] + query = ( + frappe.qb.from_(ste) + .inner_join(ste_child) + .on((ste_child.parent == ste.name)) + .select( + ste_child.item_code, + ste_child.original_item, + fn.Sum(ste_child.qty).as_("qty"), + ) + .where( + (ste.docstatus == 1) + & (ste.work_order == self.name) + & (ste.purpose == "Material Transfer for Manufacture") + & (ste.is_return == 0) + ) + .groupby(ste_child.item_code) + ) - d.db_set("transferred_qty", flt(transferred_qty), update_modified=False) + data = query.run(as_dict=1) or [] + transferred_items = frappe._dict({d.original_item or d.item_code: d.qty for d in data}) + + for row in self.required_items: + row.db_set( + "transferred_qty", (transferred_items.get(row.item_code) or 0.0), update_modified=False + ) + + def update_returned_qty(self): + ste = frappe.qb.DocType("Stock Entry") + ste_child = frappe.qb.DocType("Stock Entry Detail") + + query = ( + frappe.qb.from_(ste) + .inner_join(ste_child) + .on((ste_child.parent == ste.name)) + .select( + ste_child.item_code, + ste_child.original_item, + fn.Sum(ste_child.qty).as_("qty"), + ) + .where( + (ste.docstatus == 1) + & (ste.work_order == self.name) + & (ste.purpose == "Material Transfer for Manufacture") + & (ste.is_return == 1) + ) + .groupby(ste_child.item_code) + ) + + data = query.run(as_dict=1) or [] + returned_dict = frappe._dict({d.original_item or d.item_code: d.qty for d in data}) + + for row in self.required_items: + row.db_set("returned_qty", (returned_dict.get(row.item_code) or 0.0), update_modified=False) def update_consumed_qty_for_required_items(self): """ @@ -1470,3 +1511,25 @@ def get_reserved_qty_for_production(item_code: str, warehouse: str) -> float: ) ) ).run()[0][0] or 0.0 + + +@frappe.whitelist() +def make_stock_return_entry(work_order): + from erpnext.stock.doctype.stock_entry.stock_entry import get_available_materials + + non_consumed_items = get_available_materials(work_order) + if not non_consumed_items: + return + + wo_doc = frappe.get_cached_doc("Work Order", work_order) + + stock_entry = frappe.new_doc("Stock Entry") + stock_entry.from_bom = 1 + stock_entry.is_return = 1 + stock_entry.work_order = work_order + stock_entry.purpose = "Material Transfer for Manufacture" + stock_entry.bom_no = wo_doc.bom_no + stock_entry.add_transfered_raw_materials_in_items() + stock_entry.set_stock_entry_type() + + return stock_entry diff --git a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json index 3acf5727d1..f354d45381 100644 --- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json +++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json @@ -20,6 +20,7 @@ "column_break_11", "transferred_qty", "consumed_qty", + "returned_qty", "available_qty_at_source_warehouse", "available_qty_at_wip_warehouse" ], @@ -97,6 +98,7 @@ "fieldtype": "Column Break" }, { + "columns": 1, "depends_on": "eval:!parent.skip_transfer", "fieldname": "consumed_qty", "fieldtype": "Float", @@ -127,11 +129,19 @@ "fieldtype": "Currency", "label": "Amount", "read_only": 1 + }, + { + "columns": 1, + "fieldname": "returned_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Returned Qty ", + "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2020-04-13 18:46:32.966416", + "modified": "2022-09-28 10:50:43.512562", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order Item", @@ -140,5 +150,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js index b2428e85b7..2fb4ec6791 100644 --- a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js +++ b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js @@ -50,7 +50,7 @@ frappe.query_reports["Work Order Consumed Materials"] = { label: __("Status"), fieldname: "status", fieldtype: "Select", - options: ["In Process", "Completed", "Stopped"] + options: ["", "In Process", "Completed", "Stopped"] }, { label: __("Excess Materials Consumed"), diff --git a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py index 8158bc9a02..14e97d3dd7 100644 --- a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py +++ b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py @@ -1,6 +1,8 @@ # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +from collections import defaultdict + import frappe from frappe import _ @@ -18,7 +20,11 @@ def get_data(report_filters): filters = get_filter_condition(report_filters) wo_items = {} - for d in frappe.get_all("Work Order", filters=filters, fields=fields): + + work_orders = frappe.get_all("Work Order", filters=filters, fields=fields) + returned_materials = get_returned_materials(work_orders) + + for d in work_orders: d.extra_consumed_qty = 0.0 if d.consumed_qty and d.consumed_qty > d.required_qty: d.extra_consumed_qty = d.consumed_qty - d.required_qty @@ -39,6 +45,28 @@ def get_data(report_filters): return data +def get_returned_materials(work_orders): + raw_materials_qty = defaultdict(float) + + raw_materials = frappe.get_all( + "Stock Entry", + fields=["`tabStock Entry Detail`.`item_code`", "`tabStock Entry Detail`.`qty`"], + filters=[ + ["Stock Entry", "is_return", "=", 1], + ["Stock Entry Detail", "docstatus", "=", 1], + ["Stock Entry", "work_order", "in", [d.name for d in work_orders]], + ], + ) + + for d in raw_materials: + raw_materials_qty[d.item_code] += d.qty + + for row in work_orders: + row.returned_qty = 0.0 + if raw_materials_qty.get(row.raw_material_item_code): + row.returned_qty = raw_materials_qty.get(row.raw_material_item_code) + + def get_fields(): return [ "`tabWork Order Item`.`parent`", @@ -65,7 +93,7 @@ def get_filter_condition(report_filters): for field in ["name", "production_item", "company", "status"]: value = report_filters.get(field) if value: - key = f"`{field}`" + key = f"{field}" filters.update({key: value}) return filters @@ -112,4 +140,10 @@ def get_columns(): "fieldtype": "Float", "width": 100, }, + { + "label": _("Returned Qty"), + "fieldname": "returned_qty", + "fieldtype": "Float", + "width": 100, + }, ] diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index abe98e2933..7e9420d503 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -148,19 +148,19 @@ "search_index": 1 }, { - "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"", - "fieldname": "purchase_order", - "fieldtype": "Link", - "label": "Purchase Order", - "options": "Purchase Order" + "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"", + "fieldname": "purchase_order", + "fieldtype": "Link", + "label": "Purchase Order", + "options": "Purchase Order" }, { - "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"", - "fieldname": "subcontracting_order", - "fieldtype": "Link", - "label": "Subcontracting Order", - "options": "Subcontracting Order" - }, + "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"", + "fieldname": "subcontracting_order", + "fieldtype": "Link", + "label": "Subcontracting Order", + "options": "Subcontracting Order" + }, { "depends_on": "eval:doc.purpose==\"Sales Return\"", "fieldname": "delivery_note_no", @@ -616,6 +616,7 @@ "fieldname": "is_return", "fieldtype": "Check", "hidden": 1, + "in_list_view": 1, "label": "Is Return", "no_copy": 1, "print_hide": 1, @@ -627,7 +628,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-05-02 05:21:39.060501", + "modified": "2022-10-07 14:39:51.943770", "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 8bcd772d90..b1167351c4 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1212,13 +1212,19 @@ class StockEntry(StockController): def update_work_order(self): def _validate_work_order(pro_doc): + msg, title = "", "" if flt(pro_doc.docstatus) != 1: - frappe.throw(_("Work Order {0} must be submitted").format(self.work_order)) + msg = f"Work Order {self.work_order} must be submitted" if pro_doc.status == "Stopped": - frappe.throw( - _("Transaction not allowed against stopped Work Order {0}").format(self.work_order) - ) + msg = f"Transaction not allowed against stopped Work Order {self.work_order}" + + if self.is_return and pro_doc.status not in ["Completed", "Closed"]: + title = _("Stock Return") + msg = f"Work Order {self.work_order} must be completed or closed" + + if msg: + frappe.throw(_(msg), title=title) if self.job_card: job_doc = frappe.get_doc("Job Card", self.job_card) @@ -1754,10 +1760,12 @@ class StockEntry(StockController): for key, row in available_materials.items(): remaining_qty_to_produce = flt(wo_data.trans_qty) - flt(wo_data.produced_qty) - if remaining_qty_to_produce <= 0: + if remaining_qty_to_produce <= 0 and not self.is_return: continue - qty = (flt(row.qty) * flt(self.fg_completed_qty)) / remaining_qty_to_produce + qty = flt(row.qty) + if not self.is_return: + qty = (flt(row.qty) * flt(self.fg_completed_qty)) / remaining_qty_to_produce item = row.item_details if cint(frappe.get_cached_value("UOM", item.stock_uom, "must_be_whole_number")): @@ -1781,6 +1789,9 @@ class StockEntry(StockController): self.update_item_in_stock_entry_detail(row, item, qty) def update_item_in_stock_entry_detail(self, row, item, qty) -> None: + if not qty: + return + ste_item_details = { "from_warehouse": item.warehouse, "to_warehouse": "", @@ -1794,6 +1805,9 @@ class StockEntry(StockController): "original_item": item.original_item, } + if self.is_return: + ste_item_details["to_warehouse"] = item.s_warehouse + if row.serial_nos: serial_nos = row.serial_nos if item.batch_no: diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_list.js b/erpnext/stock/doctype/stock_entry/stock_entry_list.js index cbc3491eba..4eb0da11d2 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry_list.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry_list.js @@ -1,8 +1,13 @@ frappe.listview_settings['Stock Entry'] = { add_fields: ["`tabStock Entry`.`from_warehouse`", "`tabStock Entry`.`to_warehouse`", - "`tabStock Entry`.`purpose`", "`tabStock Entry`.`work_order`", "`tabStock Entry`.`bom_no`"], + "`tabStock Entry`.`purpose`", "`tabStock Entry`.`work_order`", "`tabStock Entry`.`bom_no`", + "`tabStock Entry`.`is_return`"], get_indicator: function (doc) { - if (doc.docstatus === 0) { + debugger + if(doc.is_return===1 && doc.purpose === "Material Transfer for Manufacture") { + return [__("Material Returned from WIP"), "orange", + "is_return,=,1|purpose,=,Material Transfer for Manufacture|docstatus,<,2"]; + } else if (doc.docstatus === 0) { return [__("Draft"), "red", "docstatus,=,0"]; } else if (doc.purpose === 'Send to Warehouse' && doc.per_transferred < 100) { From e78a70699423eb2552e77b62b3af33e5e22290da Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 7 Oct 2022 18:13:33 +0530 Subject: [PATCH 0315/1047] refactor: rewrite `Procurement Tracker Report` queries in `QB` --- .../procurement_tracker.py | 151 +++++++++--------- 1 file changed, 73 insertions(+), 78 deletions(-) diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index d70ac46ce3..71019e8037 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -127,32 +127,27 @@ def get_columns(filters): return columns -def get_conditions(filters): - conditions = "" - +def apply_filters_on_query(filters, parent, child, query): if filters.get("company"): - conditions += " AND parent.company=%s" % frappe.db.escape(filters.get("company")) + query = query.where(parent.company == filters.get("company")) if filters.get("cost_center") or filters.get("project"): - conditions += """ - AND (child.`cost_center`=%s OR child.`project`=%s) - """ % ( - frappe.db.escape(filters.get("cost_center")), - frappe.db.escape(filters.get("project")), + query = query.where( + (child.cost_center == filters.get("cost_center")) | (child.project == filters.get("project")) ) if filters.get("from_date"): - conditions += " AND parent.transaction_date>='%s'" % filters.get("from_date") + query = query.where(parent.transaction_date >= filters.get("from_date")) if filters.get("to_date"): - conditions += " AND parent.transaction_date<='%s'" % filters.get("to_date") - return conditions + query = query.where(parent.transaction_date <= filters.get("to_date")) + + return query def get_data(filters): - conditions = get_conditions(filters) - purchase_order_entry = get_po_entries(conditions) - mr_records, procurement_record_against_mr = get_mapped_mr_details(conditions) + purchase_order_entry = get_po_entries(filters) + mr_records, procurement_record_against_mr = get_mapped_mr_details(filters) pr_records = get_mapped_pr_records() pi_records = get_mapped_pi_records() @@ -187,11 +182,15 @@ def get_data(filters): return procurement_record -def get_mapped_mr_details(conditions): +def get_mapped_mr_details(filters): mr_records = {} - mr_details = frappe.db.sql( - """ - SELECT + parent = frappe.qb.DocType("Material Request") + child = frappe.qb.DocType("Material Request Item") + + query = ( + frappe.qb.from_(parent) + .from_(child) + .select( parent.transaction_date, parent.per_ordered, parent.owner, @@ -203,18 +202,13 @@ def get_mapped_mr_details(conditions): child.uom, parent.status, child.project, - child.cost_center - FROM `tabMaterial Request` parent, `tabMaterial Request Item` child - WHERE - parent.per_ordered>=0 - AND parent.name=child.parent - AND parent.docstatus=1 - {conditions} - """.format( - conditions=conditions - ), - as_dict=1, - ) # nosec + child.cost_center, + ) + .where((parent.per_ordered >= 0) & (parent.name == child.parent) & (parent.docstatus == 1)) + ) + query = apply_filters_on_query(filters, parent, child, query) + + mr_details = query.run(as_dict=True) procurement_record_against_mr = [] for record in mr_details: @@ -241,46 +235,49 @@ def get_mapped_mr_details(conditions): def get_mapped_pi_records(): - return frappe._dict( - frappe.db.sql( - """ - SELECT - pi_item.po_detail, - pi_item.base_amount - FROM `tabPurchase Invoice Item` as pi_item - INNER JOIN `tabPurchase Order` as po - ON pi_item.`purchase_order` = po.`name` - WHERE - pi_item.docstatus = 1 - AND po.status not in ('Closed','Completed','Cancelled') - AND pi_item.po_detail IS NOT NULL - """ + po = frappe.qb.DocType("Purchase Order") + pi_item = frappe.qb.DocType("Purchase Invoice Item") + pi_records = ( + frappe.qb.from_(pi_item) + .inner_join(po) + .on(pi_item.purchase_order == po.name) + .select(pi_item.po_detail, pi_item.base_amount) + .where( + (pi_item.docstatus == 1) + & (po.status.notin(("Closed", "Completed", "Cancelled"))) + & (pi_item.po_detail.isnotnull()) ) - ) + ).run() + + return frappe._dict(pi_records) def get_mapped_pr_records(): - return frappe._dict( - frappe.db.sql( - """ - SELECT - pr_item.purchase_order_item, - pr.posting_date - FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item - WHERE - pr.docstatus=1 - AND pr.name=pr_item.parent - AND pr_item.purchase_order_item IS NOT NULL - AND pr.status not in ('Closed','Completed','Cancelled') - """ + pr = frappe.qb.DocType("Purchase Receipt") + pr_item = frappe.qb.DocType("Purchase Receipt Item") + pr_records = ( + frappe.qb.from_(pr) + .from_(pr_item) + .select(pr_item.purchase_order_item, pr.posting_date) + .where( + (pr.docstatus == 1) + & (pr.name == pr_item.parent) + & (pr_item.purchase_order_item.isnotnull()) + & (pr.status.notin(("Closed", "Completed", "Cancelled"))) ) - ) + ).run() + + return frappe._dict(pr_records) -def get_po_entries(conditions): - return frappe.db.sql( - """ - SELECT +def get_po_entries(filters): + parent = frappe.qb.DocType("Purchase Order") + child = frappe.qb.DocType("Purchase Order Item") + + query = ( + frappe.qb.from_(parent) + .from_(child) + .select( child.name, child.parent, child.cost_center, @@ -297,17 +294,15 @@ def get_po_entries(conditions): parent.transaction_date, parent.supplier, parent.status, - parent.owner - FROM `tabPurchase Order` parent, `tabPurchase Order Item` child - WHERE - parent.docstatus = 1 - AND parent.name = child.parent - AND parent.status not in ('Closed','Completed','Cancelled') - {conditions} - GROUP BY - parent.name, child.item_code - """.format( - conditions=conditions - ), - as_dict=1, - ) # nosec + parent.owner, + ) + .where( + (parent.docstatus == 1) + & (parent.name == child.parent) + & (parent.status.notin(("Closed", "Completed", "Cancelled"))) + ) + .groupby(parent.name, child.item_code) + ) + query = apply_filters_on_query(filters, parent, child, query) + + return query.run(as_dict=True) From 2657ece2cdeba321426637c864ce7171e2cf4427 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 6 Oct 2022 22:22:52 +0530 Subject: [PATCH 0316/1047] fix: set Quality Inspection status based on readings status --- .../doctype/quality_inspection/quality_inspection.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index d082000434..8ffd3f2ad1 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -132,6 +132,16 @@ class QualityInspection(Document): # if not formula based check acceptance values set self.set_status_based_on_acceptance_values(reading) + if not self.manual_inspection: + self.status = "Accepted" + for reading in self.readings: + if reading.status == "Rejected": + self.status = "Rejected" + frappe.msgprint( + _("Status set to rejected as there are one or more rejected readings."), alert=True + ) + break + def set_status_based_on_acceptance_values(self, reading): if not cint(reading.numeric): result = reading.get("reading_value") == reading.get("value") From fcc1272d428c8e02b540b9b567698a1ec730fad3 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Tue, 4 Oct 2022 23:11:50 +0530 Subject: [PATCH 0317/1047] test: add test cases for Quality Inspection status --- .../test_quality_inspection.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py index 144f13880b..581aa1eff4 100644 --- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py @@ -184,6 +184,38 @@ class TestQualityInspection(FrappeTestCase): se.cancel() frappe.db.set_value("Stock Settings", None, "action_if_quality_inspection_is_rejected", "Stop") + def test_qi_status(self): + make_stock_entry( + item_code="_Test Item with QA", target="_Test Warehouse - _TC", qty=1, basic_rate=100 + ) + dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True) + qa = create_quality_inspection( + reference_type="Delivery Note", reference_name=dn.name, status="Accepted", do_not_save=True + ) + qa.readings[0].manual_inspection = 1 + qa.save() + + # Case - 1: When there are one or more readings with rejected status and parent manual inspection is unchecked, then parent status should be set to rejected. + qa.status = "Accepted" + qa.manual_inspection = 0 + qa.readings[0].status = "Rejected" + qa.save() + self.assertEqual(qa.status, "Rejected") + + # Case - 2: When all readings have accepted status and parent manual inspection is unchecked, then parent status should be set to accepted. + qa.status = "Rejected" + qa.manual_inspection = 0 + qa.readings[0].status = "Accepted" + qa.save() + self.assertEqual(qa.status, "Accepted") + + # Case - 3: When parent manual inspection is checked, then parent status should not be changed. + qa.status = "Accepted" + qa.manual_inspection = 1 + qa.readings[0].status = "Rejected" + qa.save() + self.assertEqual(qa.status, "Accepted") + def create_quality_inspection(**args): args = frappe._dict(args) From 4992e4a2b8ad4a9510485ccb214bb4484c3c8b82 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Tue, 4 Oct 2022 23:13:01 +0530 Subject: [PATCH 0318/1047] fix(test): `test_rejected_qi_validation` --- .../stock/doctype/quality_inspection/test_quality_inspection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py index 581aa1eff4..4f19643ad5 100644 --- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py @@ -160,7 +160,7 @@ class TestQualityInspection(FrappeTestCase): ) readings = [ - {"specification": "Iron Content", "min_value": 0.1, "max_value": 0.9, "reading_1": "0.4"} + {"specification": "Iron Content", "min_value": 0.1, "max_value": 0.9, "reading_1": "1.0"} ] qa = create_quality_inspection( From d806e32030ab611583fc74aa4c559518a62c76fb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 8 Oct 2022 21:25:11 +0530 Subject: [PATCH 0319/1047] fix: PO cancel post advance payment cancel against PO --- erpnext/buying/doctype/purchase_order/purchase_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index cd58d25136..bcedd4d0a1 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -349,7 +349,7 @@ class PurchaseOrder(BuyingController): update_linked_doc(self.doctype, self.name, self.inter_company_order_reference) def on_cancel(self): - self.ignore_linked_doctypes = "Payment Ledger Entry" + self.ignore_linked_doctypes = ("GL Entry", "Payment Ledger Entry") super(PurchaseOrder, self).on_cancel() if self.is_against_so(): From 537d953f4cb2c31ca225a3487a0ce6e62b6bacb8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 10 Oct 2022 10:17:19 +0530 Subject: [PATCH 0320/1047] fix: unlink payment on invoice cancellation --- erpnext/accounts/utils.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index c5eb7d8733..5b4b712b48 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -648,6 +648,16 @@ def unlink_ref_doc_from_payment_entries(ref_doc): (now(), frappe.session.user, ref_doc.doctype, ref_doc.name), ) + ple = qb.DocType("Payment Ledger Entry") + + qb.update(ple).set(ple.against_voucher_type, ple.voucher_type).set( + ple.against_voucher_no, ple.voucher_no + ).set(ple.modified, now()).set(ple.modified_by, frappe.session.user).where( + (ple.against_voucher_type == ref_doc.doctype) + & (ple.against_voucher_no == ref_doc.name) + & (ple.delinked == 0) + ).run() + if ref_doc.doctype in ("Sales Invoice", "Purchase Invoice"): ref_doc.set("advances", []) From 143f9058382852d8b2f0813d4af09e89dd817391 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 10 Oct 2022 11:34:31 +0530 Subject: [PATCH 0321/1047] test: update ple on payment unlink for SI's and SO's --- .../test_payment_ledger_entry.py | 108 +++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py b/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py index a71b19e092..fc6dbba7e7 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py +++ b/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py @@ -3,12 +3,13 @@ import frappe from frappe import qb -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import nowdate from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.stock.doctype.item.test_item import create_item @@ -127,6 +128,25 @@ class TestPaymentLedgerEntry(FrappeTestCase): payment.posting_date = posting_date return payment + def create_sales_order( + self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False + ): + so = make_sales_order( + company=self.company, + transaction_date=posting_date, + customer=self.customer, + item_code=self.item, + cost_center=self.cost_center, + warehouse=self.warehouse, + debit_to=self.debit_to, + currency="INR", + qty=qty, + rate=100, + do_not_save=do_not_save, + do_not_submit=do_not_submit, + ) + return so + def clear_old_entries(self): doctype_list = [ "GL Entry", @@ -406,3 +426,89 @@ class TestPaymentLedgerEntry(FrappeTestCase): ] self.assertEqual(pl_entries_for_crnote[0], expected_values[0]) self.assertEqual(pl_entries_for_crnote[1], expected_values[1]) + + @change_settings( + "Accounts Settings", + {"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1}, + ) + def test_multi_payment_unlink_on_invoice_cancellation(self): + transaction_date = nowdate() + amount = 100 + si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date) + + for amt in [40, 40, 20]: + # payment 1 + pe = get_payment_entry(si.doctype, si.name) + pe.paid_amount = amt + pe.get("references")[0].allocated_amount = amt + pe = pe.save().submit() + + si.reload() + si.cancel() + + entries = frappe.db.get_list( + "Payment Ledger Entry", + filters={"against_voucher_type": si.doctype, "against_voucher_no": si.name, "delinked": 0}, + ) + self.assertEqual(entries, []) + + # with references removed, deletion should be possible + si.delete() + self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, si.doctype, si.name) + + @change_settings( + "Accounts Settings", + {"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1}, + ) + def test_multi_je_unlink_on_invoice_cancellation(self): + transaction_date = nowdate() + amount = 100 + si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date) + + # multiple JE's against invoice + for amt in [40, 40, 20]: + je1 = self.create_journal_entry( + self.income_account, self.debit_to, amt, posting_date=transaction_date + ) + je1.get("accounts")[1].party_type = "Customer" + je1.get("accounts")[1].party = self.customer + je1.get("accounts")[1].reference_type = si.doctype + je1.get("accounts")[1].reference_name = si.name + je1 = je1.save().submit() + + si.reload() + si.cancel() + + entries = frappe.db.get_list( + "Payment Ledger Entry", + filters={"against_voucher_type": si.doctype, "against_voucher_no": si.name, "delinked": 0}, + ) + self.assertEqual(entries, []) + + # with references removed, deletion should be possible + si.delete() + self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, si.doctype, si.name) + + @change_settings( + "Accounts Settings", + {"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1}, + ) + def test_advance_payment_unlink_on_order_cancellation(self): + transaction_date = nowdate() + amount = 100 + so = self.create_sales_order(qty=1, rate=amount, posting_date=transaction_date).save().submit() + + pe = get_payment_entry(so.doctype, so.name).save().submit() + + so.reload() + so.cancel() + + entries = frappe.db.get_list( + "Payment Ledger Entry", + filters={"against_voucher_type": so.doctype, "against_voucher_no": so.name, "delinked": 0}, + ) + self.assertEqual(entries, []) + + # with references removed, deletion should be possible + so.delete() + self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, so.doctype, so.name) From 2d30b36ccaef9a07ca38f503c8187dc5c022ccb7 Mon Sep 17 00:00:00 2001 From: codezart Date: Mon, 10 Oct 2022 11:24:42 -0400 Subject: [PATCH 0322/1047] fix: number of months subscription plan --- .../accounts/doctype/subscription_plan/subscription_plan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py index a95e0a9c2d..dcd40b11df 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py @@ -8,6 +8,7 @@ from frappe.model.document import Document from frappe.utils import date_diff, flt, get_first_day, get_last_day, getdate from erpnext.utilities.product import get_price +from dateutil import relativedelta class SubscriptionPlan(Document): @@ -49,7 +50,7 @@ def get_plan_rate( start_date = getdate(start_date) end_date = getdate(end_date) - no_of_months = (end_date.year - start_date.year) * 12 + (end_date.month - start_date.month) + 1 + no_of_months = relativedelta.relativedelta(end_date, start_date).months + 1 cost = plan.cost * no_of_months # Adjust cost if start or end date is not month start or end From e4d7d8c42de1c087da36ac334a0518af0cb4a48b Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Mon, 10 Oct 2022 23:04:34 +0530 Subject: [PATCH 0323/1047] fix: use naming_series in budget --- erpnext/accounts/doctype/budget/budget.json | 16 +++++++++++++++- erpnext/accounts/doctype/budget/budget.py | 9 +++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/budget/budget.json b/erpnext/accounts/doctype/budget/budget.json index fc4dd200ea..f0566f4436 100644 --- a/erpnext/accounts/doctype/budget/budget.json +++ b/erpnext/accounts/doctype/budget/budget.json @@ -1,6 +1,7 @@ { "actions": [], "allow_import": 1, + "autoname": "naming_series:", "creation": "2016-05-16 11:42:29.632528", "doctype": "DocType", "editable_grid": 1, @@ -9,6 +10,7 @@ "budget_against", "company", "cost_center", + "naming_series", "project", "fiscal_year", "column_break_3", @@ -190,15 +192,26 @@ "label": "Budget Accounts", "options": "Budget Account", "reqd": 1 + }, + { + "fieldname": "naming_series", + "fieldtype": "Data", + "hidden": 1, + "label": "Series", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "set_only_once": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-10-06 15:13:54.055854", + "modified": "2022-10-10 22:14:36.361509", "modified_by": "Administrator", "module": "Accounts", "name": "Budget", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { @@ -220,5 +233,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py index 5527f9fb99..6ac3350c3b 100644 --- a/erpnext/accounts/doctype/budget/budget.py +++ b/erpnext/accounts/doctype/budget/budget.py @@ -5,7 +5,6 @@ import frappe from frappe import _ from frappe.model.document import Document -from frappe.model.naming import make_autoname from frappe.utils import add_months, flt, fmt_money, get_last_day, getdate from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( @@ -23,11 +22,6 @@ class DuplicateBudgetError(frappe.ValidationError): class Budget(Document): - def autoname(self): - self.name = make_autoname( - self.get(frappe.scrub(self.budget_against)) + "/" + self.fiscal_year + "/.###" - ) - def validate(self): if not self.get(frappe.scrub(self.budget_against)): frappe.throw(_("{0} is mandatory").format(self.budget_against)) @@ -109,6 +103,9 @@ class Budget(Document): ): self.applicable_on_booking_actual_expenses = 1 + def before_naming(self): + self.naming_series = f"{{{frappe.scrub(self.budget_against)}}}./.{self.fiscal_year}/.###" + def validate_expense_against_budget(args): args = frappe._dict(args) From 4b908ebcd6f01459bbf29db3a0863963dac45fac Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 11 Oct 2022 08:14:15 +0530 Subject: [PATCH 0324/1047] fix: value error on pos submit --- erpnext/stock/doctype/serial_no/serial_no.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 6042ed4ac5..a2748d0d09 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -846,16 +846,15 @@ def get_pos_reserved_serial_nos(filters): pos_transacted_sr_nos = query.run(as_dict=True) - reserved_sr_nos = [] - returned_sr_nos = [] + reserved_sr_nos = set() + returned_sr_nos = set() for d in pos_transacted_sr_nos: if d.is_return == 0: - reserved_sr_nos += get_serial_nos(d.serial_no) + [reserved_sr_nos.add(x) for x in get_serial_nos(d.serial_no)] elif d.is_return == 1: - returned_sr_nos += get_serial_nos(d.serial_no) + [returned_sr_nos.add(x) for x in get_serial_nos(d.serial_no)] - for sr_no in returned_sr_nos: - reserved_sr_nos.remove(sr_no) + reserved_sr_nos = list(reserved_sr_nos - returned_sr_nos) return reserved_sr_nos From 9e2bd10d032f49ed65896d7456cbc7c176b5570e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 11 Oct 2022 11:17:35 +0530 Subject: [PATCH 0325/1047] test: value error on serial no validation on pos --- .../doctype/pos_invoice/test_pos_invoice.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 70f128e0e3..3132fdd259 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -495,6 +495,67 @@ class TestPOSInvoice(unittest.TestCase): self.assertRaises(frappe.ValidationError, pos.submit) + def test_value_error_on_serial_no_validation(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item + + se = make_serialized_item( + company="_Test Company", + target_warehouse="Stores - _TC", + cost_center="Main - _TC", + expense_account="Cost of Goods Sold - _TC", + ) + serial_nos = se.get("items")[0].serial_no + + # make a pos invoice + pos = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + rate=1000, + qty=1, + do_not_save=1, + ) + pos.get("items")[0].has_serial_no = 1 + pos.get("items")[0].serial_no = serial_nos.split("\n")[0] + pos.set("payments", []) + pos.append( + "payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000, "default": 1} + ) + pos = pos.save().submit() + + # make a return + pos_return = make_sales_return(pos.name) + pos_return.paid_amount = pos_return.grand_total + pos_return.save() + pos_return.submit() + + # set docstatus to 2 for pos to trigger this issue + frappe.db.set_value("POS Invoice", pos.name, "docstatus", 2) + + pos2 = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + rate=1000, + qty=1, + do_not_save=1, + ) + + pos2.get("items")[0].has_serial_no = 1 + pos2.get("items")[0].serial_no = serial_nos.split("\n")[0] + # Value error should not be triggered on validation + pos2.save() + def test_loyalty_points(self): from erpnext.accounts.doctype.loyalty_program.loyalty_program import ( get_loyalty_program_details_with_points, From f6613e1e4c53821bd0924539936b8a70d513773f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 27 Sep 2022 14:19:29 +0530 Subject: [PATCH 0326/1047] feat: Tab Break in Sales Order, Delivery Note, Sales Invoice and Purchase Order --- .../doctype/sales_invoice/sales_invoice.json | 354 +++++++++++------- .../purchase_order/purchase_order.json | 135 ++++--- .../doctype/sales_order/sales_order.json | 231 ++++++++---- .../doctype/delivery_note/delivery_note.json | 282 ++++++++------ 4 files changed, 631 insertions(+), 371 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 2da515737a..d8b413d9ff 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -12,44 +12,29 @@ "customer", "customer_name", "tax_id", - "pos_profile", - "is_pos", - "is_consolidated", - "is_return", - "is_debit_note", - "update_billed_amount_in_sales_order", "column_break1", - "company", - "company_tax_id", "posting_date", "posting_time", "set_posting_time", "due_date", + "column_break_14", + "company", + "company_tax_id", + "is_pos", + "pos_profile", + "is_consolidated", + "is_return", "return_against", + "update_billed_amount_in_sales_order", + "is_debit_note", "amended_from", "accounting_dimensions_section", - "project", - "dimension_col_break", "cost_center", - "customer_po_details", - "po_no", - "column_break_23", - "po_date", - "address_and_contact", - "customer_address", - "address_display", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "territory", - "col_break4", - "shipping_address_name", - "shipping_address", - "company_address", - "company_address_display", - "dispatch_address_name", - "dispatch_address", + "dimension_col_break", + "project", + "column_break_27", + "campaign", + "source", "currency_and_price_list", "currency", "conversion_rate", @@ -58,60 +43,35 @@ "price_list_currency", "plc_conversion_rate", "ignore_pricing_rule", - "sec_warehouse", - "set_warehouse", - "column_break_55", - "set_target_warehouse", "items_section", - "update_stock", "scan_barcode", + "update_stock", + "column_break_39", + "set_warehouse", + "set_target_warehouse", + "section_break_42", "items", - "pricing_rule_details", - "pricing_rules", - "packing_list", - "packed_items", - "product_bundle_help", - "time_sheet_list", - "timesheets", - "total_billing_amount", - "total_billing_hours", "section_break_30", "total_qty", + "total_net_weight", + "column_break_32", "base_total", "base_net_total", - "column_break_32", - "total_net_weight", + "column_break_52", "total", "net_total", "taxes_section", "taxes_and_charges", "column_break_38", "shipping_rule", + "column_break_55", "tax_category", "section_break_40", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "section_break_43", "base_total_taxes_and_charges", "column_break_47", "total_taxes_and_charges", - "loyalty_points_redemption", - "loyalty_points", - "loyalty_amount", - "redeem_loyalty_points", - "column_break_77", - "loyalty_program", - "loyalty_redemption_account", - "loyalty_redemption_cost_center", - "section_break_49", - "apply_discount_on", - "is_cash_or_non_trade_discount", - "base_discount_amount", - "additional_discount_account", - "column_break_51", - "additional_discount_percentage", - "discount_amount", "totals", "base_grand_total", "base_rounding_adjustment", @@ -125,21 +85,28 @@ "total_advance", "outstanding_amount", "disable_rounded_total", - "column_break4", - "write_off_amount", - "base_write_off_amount", - "write_off_outstanding_amount_automatically", - "column_break_74", - "write_off_account", - "write_off_cost_center", - "advances_section", - "allocate_advances_automatically", - "get_advances", - "advances", - "payment_schedule_section", - "ignore_default_payment_terms_template", - "payment_terms_template", - "payment_schedule", + "section_break_49", + "apply_discount_on", + "is_cash_or_non_trade_discount", + "base_discount_amount", + "additional_discount_account", + "column_break_51", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "pricing_rule_details", + "pricing_rules", + "packing_list", + "packed_items", + "product_bundle_help", + "time_sheet_list", + "timesheets", + "section_break_104", + "total_billing_hours", + "column_break_106", + "total_billing_amount", + "payments_tab", "payments_section", "cash_bank_account", "payments", @@ -152,47 +119,95 @@ "column_break_90", "change_amount", "account_for_change_amount", + "advances_section", + "allocate_advances_automatically", + "get_advances", + "advances", + "write_off_section", + "write_off_amount", + "base_write_off_amount", + "write_off_outstanding_amount_automatically", + "column_break_74", + "write_off_account", + "write_off_cost_center", + "loyalty_points_redemption", + "redeem_loyalty_points", + "loyalty_points", + "loyalty_amount", + "column_break_77", + "loyalty_program", + "loyalty_redemption_account", + "loyalty_redemption_cost_center", + "contact_and_address_tab", + "address_and_contact", + "customer_address", + "address_display", + "col_break4", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "territory", + "shipping_address_section", + "shipping_address_name", + "shipping_address", + "shipping_addr_col_break", + "dispatch_address_name", + "dispatch_address", + "company_address_section", + "company_address", + "company_addr_col_break", + "company_address_display", + "terms_tab", + "payment_schedule_section", + "ignore_default_payment_terms_template", + "payment_terms_template", + "payment_schedule", "terms_section_break", "tc_name", "terms", - "edit_printing_settings", - "letter_head", - "group_same_items", - "select_print_heading", - "column_break_84", - "language", - "more_information", - "status", - "inter_company_invoice_reference", - "represents_company", - "customer_group", - "campaign", - "col_break23", - "is_internal_customer", - "is_discounted", - "source", + "more_info_tab", + "customer_po_details", + "po_no", + "column_break_23", + "po_date", "more_info", "debit_to", "party_account_currency", "is_opening", "column_break8", "unrealized_profit_loss_account", - "remarks", + "against_income_account", "sales_team_section_break", "sales_partner", - "column_break10", "amount_eligible_for_commission", + "column_break10", "commission_rate", "total_commission", "section_break2", "sales_team", + "edit_printing_settings", + "letter_head", + "group_same_items", + "column_break_84", + "select_print_heading", + "language", "subscription_section", "from_date", - "to_date", - "column_break_140", "auto_repeat", + "column_break_140", + "to_date", "update_auto_repeat_reference", - "against_income_account" + "more_information", + "status", + "inter_company_invoice_reference", + "represents_company", + "customer_group", + "col_break23", + "is_internal_customer", + "is_discounted", + "remarks", + "connections_tab" ], "fields": [ { @@ -453,12 +468,11 @@ "label": "Customer's Purchase Order Date" }, { - "collapsible": 1, "fieldname": "address_and_contact", "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Address and Contact" + "label": "Billing Address" }, { "fieldname": "customer_address", @@ -560,7 +574,6 @@ { "fieldname": "company_address_display", "fieldtype": "Small Text", - "hidden": 1, "hide_days": 1, "hide_seconds": 1, "label": "Company Address", @@ -651,13 +664,6 @@ "label": "Ignore Pricing Rule", "print_hide": 1 }, - { - "fieldname": "sec_warehouse", - "fieldtype": "Section Break", - "hide_days": 1, - "hide_seconds": 1, - "label": "Warehouse" - }, { "depends_on": "update_stock", "fieldname": "set_warehouse", @@ -671,6 +677,7 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1, "label": "Items", @@ -702,7 +709,6 @@ "fieldtype": "Table", "hide_days": 1, "hide_seconds": 1, - "label": "Items", "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Sales Invoice Item", @@ -755,9 +761,10 @@ { "collapsible": 1, "collapsible_depends_on": "eval:doc.total_billing_amount > 0", - "depends_on": "eval: !doc.is_return", + "depends_on": "eval:!doc.is_return", "fieldname": "time_sheet_list", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1, "label": "Time Sheet List" @@ -856,8 +863,10 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1, + "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -900,6 +909,7 @@ { "fieldname": "section_break_40", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1 }, @@ -908,7 +918,6 @@ "fieldtype": "Table", "hide_days": 1, "hide_seconds": 1, - "label": "Sales Taxes and Charges", "oldfieldname": "other_charges", "oldfieldtype": "Table", "options": "Sales Taxes and Charges" @@ -1314,6 +1323,8 @@ "print_hide": 1 }, { + "collapsible": 1, + "collapsible_depends_on": "eval:!doc.is_pos", "depends_on": "eval:doc.is_pos===1||(doc.advances && doc.advances.length>0)", "fieldname": "payments_section", "fieldtype": "Section Break", @@ -1383,10 +1394,13 @@ "read_only": 1 }, { + "collapsible": 1, + "depends_on": "is_pos", "fieldname": "section_break_88", "fieldtype": "Section Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "label": "Changes" }, { "depends_on": "is_pos", @@ -1427,17 +1441,6 @@ "options": "Account", "print_hide": 1 }, - { - "collapsible": 1, - "collapsible_depends_on": "write_off_amount", - "depends_on": "grand_total", - "fieldname": "column_break4", - "fieldtype": "Section Break", - "hide_days": 1, - "hide_seconds": 1, - "label": "Write Off", - "width": "50%" - }, { "fieldname": "write_off_amount", "fieldtype": "Currency", @@ -1530,7 +1533,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Printing Settings" + "label": "Print Settings" }, { "allow_on_submit": 1, @@ -1591,7 +1594,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "More Information" + "label": "Additional Info" }, { "fieldname": "inter_company_invoice_reference", @@ -1814,7 +1817,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Subscription Section" + "label": "Subscription" }, { "allow_on_submit": 1, @@ -1935,10 +1938,6 @@ "options": "Company", "read_only": 1 }, - { - "fieldname": "column_break_55", - "fieldtype": "Column Break" - }, { "depends_on": "eval: doc.is_internal_customer && doc.update_stock", "fieldname": "set_target_warehouse", @@ -2009,6 +2008,97 @@ "fieldname": "is_cash_or_non_trade_discount", "fieldtype": "Check", "label": "Is Cash or Non Trade Discount" + }, + { + "fieldname": "contact_and_address_tab", + "fieldtype": "Tab Break", + "label": "Contact & Address" + }, + { + "fieldname": "payments_tab", + "fieldtype": "Tab Break", + "label": "Payments" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_39", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_42", + "fieldtype": "Section Break", + "hide_border": 1, + "hide_days": 1, + "hide_seconds": 1 + }, + { + "fieldname": "column_break_55", + "fieldtype": "Column Break" + }, + { + "fieldname": "shipping_address_section", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "company_address_section", + "fieldtype": "Section Break", + "label": "Company Address" + }, + { + "fieldname": "shipping_addr_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_addr_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_27", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_52", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:(!doc.is_return && doc.total_billing_amount > 0)", + "fieldname": "section_break_104", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_106", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "collapsible_depends_on": "write_off_amount", + "depends_on": "grand_total", + "fieldname": "write_off_section", + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, + "label": "Write Off", + "width": "50%" } ], "icon": "fa fa-file-text", @@ -2021,7 +2111,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2022-09-16 17:44:22.227332", + "modified": "2022-09-27 14:15:35.293825", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index fb8f25a0df..7f0fc57974 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -12,43 +12,24 @@ "title", "naming_series", "supplier", - "get_items_from_open_material_requests", "supplier_name", - "apply_tds", - "tax_withholding_category", - "is_subcontracted", - "supplier_warehouse", - "column_break1", - "company", + "get_items_from_open_material_requests", + "column_break_7", "transaction_date", "schedule_date", "order_confirmation_no", "order_confirmation_date", + "column_break1", + "company", + "apply_tds", + "tax_withholding_category", + "is_subcontracted", + "supplier_warehouse", "amended_from", "accounting_dimensions_section", "cost_center", "dimension_col_break", "project", - "drop_ship", - "customer", - "customer_name", - "column_break_19", - "customer_contact_person", - "customer_contact_display", - "customer_contact_mobile", - "customer_contact_email", - "section_addresses", - "supplier_address", - "address_display", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "col_break_address", - "shipping_address", - "shipping_address_display", - "billing_address", - "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -57,7 +38,6 @@ "price_list_currency", "plc_conversion_rate", "ignore_pricing_rule", - "section_break_45", "before_items_section", "scan_barcode", "set_from_warehouse", @@ -67,10 +47,11 @@ "items", "sb_last_purchase", "total_qty", + "total_net_weight", + "column_break_40", "base_total", "base_net_total", "column_break_26", - "total_net_weight", "total", "net_total", "section_break_48", @@ -79,14 +60,13 @@ "set_reserve_warehouse", "supplied_items", "taxes_section", + "taxes_and_charges", + "column_break_53", "tax_category", "column_break_50", "shipping_rule", "section_break_52", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "totals", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", @@ -95,12 +75,6 @@ "taxes_and_charges_added", "taxes_and_charges_deducted", "total_taxes_and_charges", - "discount_section", - "apply_discount_on", - "base_discount_amount", - "column_break_45", - "additional_discount_percentage", - "discount_amount", "totals_section", "base_grand_total", "base_rounding_adjustment", @@ -113,17 +87,48 @@ "disable_rounded_total", "in_words", "advance_paid", + "discount_section", + "apply_discount_on", + "base_discount_amount", + "column_break_45", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "contacts_and_addresses_tab", + "section_addresses", + "supplier_address", + "address_display", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "col_break_address", + "shipping_address", + "shipping_address_display", + "billing_address", + "billing_address_display", + "terms_tab", + "terms_section_break", + "tc_name", + "terms", "payment_schedule_section", "payment_terms_template", "payment_schedule", + "drop_ship", + "customer", + "customer_name", + "column_break_19", + "customer_contact_person", + "customer_contact_display", + "customer_contact_mobile", + "customer_contact_email", + "more_info_tab", "tracking_section", "status", "column_break_75", "per_billed", "per_received", - "terms_section_break", - "tc_name", - "terms", "column_break5", "letter_head", "select_print_heading", @@ -143,7 +148,8 @@ "is_internal_supplier", "represents_company", "inter_company_order_reference", - "is_old_subcontracting_flow" + "is_old_subcontracting_flow", + "dashboard" ], "fields": [ { @@ -483,7 +489,6 @@ "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", - "label": "Items", "oldfieldname": "po_details", "oldfieldtype": "Table", "options": "Purchase Order Item", @@ -579,6 +584,8 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -633,7 +640,6 @@ { "fieldname": "totals", "fieldtype": "Section Break", - "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -1098,7 +1104,9 @@ }, { "fieldname": "before_items_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items" }, { "fieldname": "items_col_break", @@ -1133,10 +1141,6 @@ "label": "Tax Withholding Category", "options": "Tax Withholding Category" }, - { - "fieldname": "section_break_45", - "fieldtype": "Section Break" - }, { "collapsible": 1, "fieldname": "accounting_dimensions_section", @@ -1173,6 +1177,39 @@ "fieldtype": "Link", "label": "Set From Warehouse", "options": "Warehouse" + }, + { + "fieldname": "contacts_and_addresses_tab", + "fieldtype": "Tab Break", + "label": "Contacts & Addresses" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "dashboard", + "fieldtype": "Tab Break", + "label": "Dashboard", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_40", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_53", + "fieldtype": "Column Break" } ], "icon": "fa fa-file-text", diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index ff269d0e68..5013d51a8a 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -15,37 +15,25 @@ "naming_series", "customer", "customer_name", + "tax_id", "order_type", - "skip_delivery_note", - "column_break1", - "amended_from", - "company", + "column_break_7", "transaction_date", "delivery_date", + "column_break1", "po_no", "po_date", - "tax_id", + "company", + "skip_delivery_note", + "amended_from", "accounting_dimensions_section", "cost_center", "dimension_col_break", "project", - "contact_info", - "customer_address", - "address_display", - "contact_person", - "contact_display", - "contact_phone", - "contact_mobile", - "contact_email", - "company_address", - "company_address_display", - "col_break46", - "shipping_address_name", - "shipping_address", - "dispatch_address_name", - "dispatch_address", - "customer_group", - "territory", + "column_break_77", + "source", + "campaign", + "custom_dimensions_section", "currency_and_price_list", "currency", "conversion_rate", @@ -55,46 +43,32 @@ "plc_conversion_rate", "ignore_pricing_rule", "sec_warehouse", + "scan_barcode", + "column_break_28", "set_warehouse", "items_section", - "scan_barcode", "items", - "packing_list", - "packed_items", - "pricing_rule_details", - "pricing_rules", "section_break_31", - "column_break_33a", "total_qty", + "total_net_weight", + "column_break_33", "base_total", "base_net_total", - "column_break_33", - "total_net_weight", + "column_break_33a", "total", "net_total", "taxes_section", - "tax_category", + "taxes_and_charges", "column_break_38", + "tax_category", + "column_break_49", "shipping_rule", "section_break_40", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "section_break_43", "base_total_taxes_and_charges", "column_break_46", "total_taxes_and_charges", - "loyalty_points_redemption", - "loyalty_points", - "loyalty_amount", - "section_break_48", - "coupon_code", - "apply_discount_on", - "base_discount_amount", - "column_break_50", - "additional_discount_percentage", - "discount_amount", "totals", "base_grand_total", "base_rounding_adjustment", @@ -107,26 +81,49 @@ "in_words", "advance_paid", "disable_rounded_total", + "section_break_48", + "coupon_code", + "apply_discount_on", + "base_discount_amount", + "column_break_50", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "packing_list", + "packed_items", + "pricing_rule_details", + "pricing_rules", + "contact_info", + "billing_address_column", + "customer_address", + "address_display", + "customer_group", + "territory", + "column_break_84", + "contact_person", + "contact_display", + "contact_phone", + "contact_mobile", + "contact_email", + "shipping_address_column", + "shipping_address_name", + "shipping_address", + "column_break_93", + "dispatch_address_name", + "dispatch_address", + "col_break46", + "company_address", + "column_break_92", + "company_address_display", "payment_schedule_section", + "payment_terms_section", "payment_terms_template", "payment_schedule", "terms_section_break", "tc_name", "terms", "more_info", - "is_internal_customer", - "represents_company", - "inter_company_order_reference", - "party_account_currency", - "column_break_77", - "source", - "campaign", - "printing_details", - "language", - "letter_head", - "column_break4", - "select_print_heading", - "group_same_items", "section_break_78", "status", "delivery_status", @@ -143,12 +140,29 @@ "total_commission", "section_break1", "sales_team", + "loyalty_points_redemption", + "loyalty_points", + "column_break_116", + "loyalty_amount", "subscription_section", "from_date", "to_date", "column_break_108", "auto_repeat", - "update_auto_repeat_reference" + "update_auto_repeat_reference", + "printing_details", + "language", + "letter_head", + "group_same_items", + "column_break4", + "select_print_heading", + "additional_info_section", + "is_internal_customer", + "represents_company", + "column_break_152", + "inter_company_order_reference", + "party_account_currency", + "connections_tab" ], "fields": [ { @@ -334,10 +348,10 @@ "collapsible": 1, "depends_on": "customer", "fieldname": "contact_info", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "hide_days": 1, "hide_seconds": 1, - "label": "Address and Contact", + "label": "Address & Contact", "options": "fa fa-bullhorn" }, { @@ -414,9 +428,10 @@ }, { "fieldname": "col_break46", - "fieldtype": "Column Break", + "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, + "label": "Company Address", "width": "50%" }, { @@ -550,8 +565,10 @@ { "fieldname": "sec_warehouse", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "label": "Items" }, { "fieldname": "set_warehouse", @@ -565,6 +582,7 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1, "oldfieldtype": "Section Break", @@ -688,7 +706,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Taxes and Charges", + "label": "Taxes", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -720,6 +738,7 @@ { "fieldname": "section_break_40", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1 }, @@ -804,7 +823,7 @@ "hidden": 1, "hide_days": 1, "hide_seconds": 1, - "label": "Loyalty Points Redemption", + "label": "Loyalty Points", "print_hide": 1 }, { @@ -833,7 +852,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Additional Discount and Coupon Code" + "label": "Additional Discount" }, { "fieldname": "coupon_code", @@ -891,6 +910,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, + "label": "Totals", "oldfieldtype": "Section Break", "options": "fa fa-money", "print_hide": 1 @@ -1045,10 +1065,10 @@ }, { "fieldname": "payment_schedule_section", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "hide_days": 1, "hide_seconds": 1, - "label": "Payment Terms" + "label": "Terms" }, { "fieldname": "payment_terms_template", @@ -1070,13 +1090,12 @@ "print_hide": 1 }, { - "collapsible": 1, "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Terms and Conditions", + "label": "Terms & Conditions", "oldfieldtype": "Section Break", "options": "fa fa-legal" }, @@ -1104,10 +1123,10 @@ "collapsible": 1, "collapsible_depends_on": "project", "fieldname": "more_info", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "hide_days": 1, "hide_seconds": 1, - "label": "More Information", + "label": "More Info", "oldfieldtype": "Section Break", "options": "fa fa-file-text", "print_hide": 1 @@ -1122,7 +1141,6 @@ "read_only": 1 }, { - "description": "Track this Sales Order against any Project", "fieldname": "project", "fieldtype": "Link", "hide_days": 1, @@ -1240,7 +1258,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Billing and Delivery Status", + "label": "Status", "oldfieldtype": "Column Break", "print_hide": 1, "width": "50%" @@ -1410,7 +1428,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Auto Repeat Section", + "label": "Auto Repeat", "no_copy": 1, "print_hide": 1, "read_only": 1 @@ -1542,13 +1560,76 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_49", + "fieldtype": "Column Break" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "payment_terms_section", + "fieldtype": "Section Break", + "label": "Payment Terms" + }, + { + "fieldname": "column_break_116", + "fieldtype": "Column Break" + }, + { + "fieldname": "billing_address_column", + "fieldtype": "Section Break", + "label": "Billing Address" + }, + { + "fieldname": "shipping_address_column", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "column_break_93", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_84", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_92", + "fieldtype": "Column Break" + }, + { + "fieldname": "custom_dimensions_section", + "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info" + }, + { + "fieldname": "column_break_152", + "fieldtype": "Column Break" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:43:57.007441", + "modified": "2022-09-27 12:38:57.007441", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index a8f907ed71..519301c304 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -8,46 +8,29 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ - "delivery_to_section", - "column_break0", "title", "naming_series", "customer", + "tax_id", "customer_name", "column_break1", - "amended_from", - "company", "posting_date", "posting_time", "set_posting_time", + "column_break_10", + "company", + "amended_from", "is_return", "issue_credit_note", "return_against", "accounting_dimensions_section", "cost_center", - "dimension_col_break", + "column_break_18", "project", - "customer_po_details", - "po_no", - "column_break_17", - "po_date", - "section_break_18", - "pick_list", - "contact_info", - "shipping_address_name", - "shipping_address", - "dispatch_address_name", - "dispatch_address", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "col_break21", - "customer_address", - "tax_id", - "address_display", - "company_address", - "company_address_display", + "dimension_col_break", + "campaign", + "source", + "custom_dimensions_section", "currency_and_price_list", "currency", "conversion_rate", @@ -56,45 +39,36 @@ "price_list_currency", "plc_conversion_rate", "ignore_pricing_rule", - "sec_warehouse", - "set_warehouse", - "col_break_warehouse", - "set_target_warehouse", "items_section", "scan_barcode", + "column_break_25", + "pick_list", + "col_break_warehouse", + "set_warehouse", + "set_target_warehouse", + "section_break_30", "items", - "pricing_rule_details", - "pricing_rules", - "packing_list", - "packed_items", - "product_bundle_help", "section_break_31", "total_qty", + "total_net_weight", + "column_break_35", "base_total", "base_net_total", "column_break_33", - "total_net_weight", "total", "net_total", "taxes_section", + "taxes_and_charges", + "column_break_43", "tax_category", "column_break_39", "shipping_rule", "section_break_41", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "section_break_44", "base_total_taxes_and_charges", "column_break_47", "total_taxes_and_charges", - "section_break_49", - "apply_discount_on", - "base_discount_amount", - "column_break_51", - "additional_discount_percentage", - "discount_amount", "totals", "base_grand_total", "base_rounding_adjustment", @@ -106,9 +80,50 @@ "rounded_total", "in_words", "disable_rounded_total", - "terms_section_break", + "section_break_49", + "apply_discount_on", + "base_discount_amount", + "column_break_51", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "packing_list", + "packed_items", + "product_bundle_help", + "pricing_rule_details", + "pricing_rules", + "address_and_contact_tab", + "contact_info", + "customer_address", + "address_display", + "col_break21", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "shipping_address_section", + "shipping_address_name", + "shipping_address", + "column_break_95", + "dispatch_address_name", + "dispatch_address", + "company_address_section", + "company_address", + "column_break_101", + "company_address_display", + "terms_tab", "tc_name", "terms", + "more_info_tab", + "section_break_83", + "per_billed", + "status", + "column_break_112", + "per_installed", + "installation_status", + "column_break_89", + "per_returned", "transporter_info", "transporter", "driver", @@ -118,56 +133,39 @@ "transporter_name", "driver_name", "lr_date", - "more_info", - "campaign", - "source", - "column_break5", - "is_internal_customer", - "represents_company", - "inter_company_reference", - "per_billed", - "customer_group", - "territory", - "printing_details", - "letter_head", - "select_print_heading", - "language", - "column_break_88", - "print_without_amount", - "group_same_items", - "section_break_83", - "status", - "per_installed", - "installation_status", - "column_break_89", - "per_returned", - "excise_page", - "instructions", - "subscription_section", - "auto_repeat", + "customer_po_details", + "po_no", + "column_break_17", + "po_date", "sales_team_section_break", "sales_partner", - "column_break7", "amount_eligible_for_commission", + "column_break7", "commission_rate", "total_commission", "section_break1", - "sales_team" + "sales_team", + "subscription_section", + "auto_repeat", + "printing_details", + "letter_head", + "select_print_heading", + "column_break_88", + "language", + "print_without_amount", + "group_same_items", + "more_info", + "is_internal_customer", + "represents_company", + "inter_company_reference", + "customer_group", + "territory", + "column_break5", + "excise_page", + "instructions", + "connections_tab" ], "fields": [ - { - "fieldname": "delivery_to_section", - "fieldtype": "Section Break", - "label": "Delivery To", - "options": "fa fa-user" - }, - { - "fieldname": "column_break0", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "print_width": "50%", - "width": "50%" - }, { "allow_on_submit": 1, "default": "{customer_name}", @@ -337,11 +335,10 @@ "width": "100px" }, { - "collapsible": 1, "depends_on": "customer", "fieldname": "contact_info", "fieldtype": "Section Break", - "label": "Address and Contact", + "label": "Billing Address", "options": "fa fa-bullhorn" }, { @@ -493,10 +490,6 @@ "permlevel": 1, "print_hide": 1 }, - { - "fieldname": "sec_warehouse", - "fieldtype": "Section Break" - }, { "fieldname": "set_warehouse", "fieldtype": "Link", @@ -511,6 +504,8 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items", "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -524,7 +519,6 @@ "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", - "label": "Items", "oldfieldname": "delivery_note_details", "oldfieldtype": "Table", "options": "Delivery Note Item", @@ -628,6 +622,7 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" @@ -653,10 +648,10 @@ }, { "fieldname": "section_break_41", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { - "description": "If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.", "fieldname": "taxes_and_charges", "fieldtype": "Link", "label": "Sales Taxes and Charges Template", @@ -668,7 +663,6 @@ { "fieldname": "taxes", "fieldtype": "Table", - "label": "Sales Taxes and Charges", "oldfieldname": "other_charges", "oldfieldtype": "Table", "options": "Sales Taxes and Charges" @@ -759,6 +753,7 @@ { "fieldname": "totals", "fieldtype": "Section Break", + "label": "Totals", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -863,15 +858,6 @@ "read_only": 1, "width": "150px" }, - { - "collapsible": 1, - "collapsible_depends_on": "terms", - "fieldname": "terms_section_break", - "fieldtype": "Section Break", - "label": "Terms and Conditions", - "oldfieldtype": "Section Break", - "options": "fa fa-legal" - }, { "fieldname": "tc_name", "fieldtype": "Link", @@ -965,13 +951,12 @@ "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", - "label": "More Information", + "label": "Additional Info", "oldfieldtype": "Section Break", "options": "fa fa-file-text", "print_hide": 1 }, { - "description": "Track this Delivery Note against any Project", "fieldname": "project", "fieldtype": "Link", "label": "Project", @@ -1110,7 +1095,6 @@ }, { "depends_on": "eval:!doc.__islocal", - "description": "% of materials delivered against this Delivery Note", "fieldname": "per_installed", "fieldtype": "Percent", "in_list_view": 1, @@ -1237,10 +1221,6 @@ "options": "Pick List", "read_only": 1 }, - { - "fieldname": "section_break_18", - "fieldtype": "Section Break" - }, { "default": "0", "fetch_from": "customer.is_internal_customer", @@ -1329,13 +1309,85 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_25", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_30", + "fieldtype": "Section Break", + "hide_border": 1 + }, + { + "fieldname": "column_break_35", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_43", + "fieldtype": "Column Break" + }, + { + "fieldname": "custom_dimensions_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "shipping_address_section", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "column_break_95", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_address_section", + "fieldtype": "Section Break", + "label": "Company Address" + }, + { + "fieldname": "column_break_101", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_112", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" } ], "icon": "fa fa-truck", "idx": 146, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:46:17.701904", + "modified": "2022-09-27 12:49:21.640004", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", From 754656213982096f5b108635ac33c7e9f212746c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 27 Sep 2022 15:16:27 +0530 Subject: [PATCH 0327/1047] feat: Tab Break in Quotation --- .../selling/doctype/quotation/quotation.json | 225 ++++++++++++------ .../doctype/sales_order/sales_order.json | 3 +- .../doctype/delivery_note/delivery_note.json | 3 +- 3 files changed, 151 insertions(+), 80 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index c58a46ba51..78b739cedb 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -15,26 +15,13 @@ "quotation_to", "party_name", "customer_name", - "column_break1", - "amended_from", - "company", + "column_break_7", "transaction_date", "valid_till", + "column_break1", "order_type", - "contact_section", - "customer_address", - "address_display", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "col_break98", - "shipping_address_name", - "shipping_address", - "company_address", - "company_address_display", - "customer_group", - "territory", + "company", + "amended_from", "currency_and_price_list", "currency", "conversion_rate", @@ -43,59 +30,79 @@ "price_list_currency", "plc_conversion_rate", "ignore_pricing_rule", - "section_break_33", - "scan_barcode", "items_section", + "scan_barcode", "items", - "bundle_items_section", - "packed_items", - "pricing_rule_details", - "pricing_rules", "sec_break23", "total_qty", + "total_net_weight", + "column_break_28", "base_total", "base_net_total", - "column_break_28", + "column_break_31", "total", "net_total", - "total_net_weight", "taxes_section", + "taxes_and_charges", + "column_break_36", "tax_category", "column_break_34", "shipping_rule", "section_break_36", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "section_break_39", "base_total_taxes_and_charges", "column_break_42", "total_taxes_and_charges", - "section_break_44", - "coupon_code", - "referral_sales_partner", - "apply_discount_on", - "base_discount_amount", - "column_break_46", - "additional_discount_percentage", - "discount_amount", "totals", "base_grand_total", "base_rounding_adjustment", - "base_in_words", "base_rounded_total", + "base_in_words", "column_break3", "grand_total", "rounding_adjustment", "rounded_total", "in_words", + "section_break_44", + "apply_discount_on", + "base_discount_amount", + "coupon_code", + "column_break_46", + "additional_discount_percentage", + "discount_amount", + "referral_sales_partner", + "sec_tax_breakup", + "other_charges_calculation", + "bundle_items_section", + "packed_items", + "pricing_rule_details", + "pricing_rules", + "address_and_contact_tab", + "billing_address_section", + "customer_address", + "address_display", + "col_break98", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "shipping_address_section", + "shipping_address_name", + "column_break_81", + "shipping_address", + "company_address_section", + "company_address", + "column_break_87", + "company_address_display", + "terms_tab", "payment_schedule_section", "payment_terms_template", "payment_schedule", "terms_section_break", "tc_name", "terms", + "more_info_tab", "print_settings", "letter_head", "group_same_items", @@ -105,17 +112,23 @@ "subscription_section", "auto_repeat", "update_auto_repeat_reference", - "more_info", + "additional_info_section", + "status", + "customer_group", + "territory", + "column_break_108", "campaign", "source", - "order_lost_reason", "column_break4", - "status", - "enq_det", - "supplier_quotation", "opportunity", + "supplier_quotation", + "enq_det", + "lost_reasons_section", "lost_reasons", - "competitors" + "competitors", + "column_break_117", + "order_lost_reason", + "connections_tab" ], "fields": [ { @@ -241,14 +254,6 @@ "print_hide": 1, "reqd": 1 }, - { - "collapsible": 1, - "depends_on": "party_name", - "fieldname": "contact_section", - "fieldtype": "Section Break", - "label": "Address and Contact", - "options": "fa fa-bullhorn" - }, { "fieldname": "customer_address", "fieldtype": "Link", @@ -408,6 +413,8 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items", "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -415,7 +422,6 @@ "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", - "label": "Items", "oldfieldname": "quotation_details", "oldfieldtype": "Table", "options": "Quotation Item", @@ -423,6 +429,7 @@ "width": "40px" }, { + "collapsible": 1, "fieldname": "pricing_rule_details", "fieldtype": "Section Break", "label": "Pricing Rules" @@ -492,6 +499,7 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" @@ -517,7 +525,8 @@ }, { "fieldname": "section_break_36", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "taxes_and_charges", @@ -579,10 +588,9 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_44", "fieldtype": "Section Break", - "label": "Additional Discount and Coupon Code" + "label": "Additional Discount" }, { "fieldname": "coupon_code", @@ -632,6 +640,7 @@ { "fieldname": "totals", "fieldtype": "Section Break", + "label": "Totals", "oldfieldtype": "Section Break", "options": "fa fa-money", "print_hide": 1 @@ -657,7 +666,6 @@ "read_only": 1 }, { - "description": "In Words will be visible once you save the Quotation.", "fieldname": "base_in_words", "fieldtype": "Data", "label": "In Words (Company Currency)", @@ -665,8 +673,7 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "width": "200px" + "read_only": 1 }, { "fieldname": "base_rounded_total", @@ -749,7 +756,6 @@ "print_hide": 1 }, { - "collapsible": 1, "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", @@ -824,7 +830,7 @@ { "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Auto Repeat Section" + "label": "Auto Repeat" }, { "fieldname": "auto_repeat", @@ -842,15 +848,6 @@ "fieldtype": "Button", "label": "Update Auto Repeat Reference" }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "print_hide": 1 - }, { "fieldname": "campaign", "fieldtype": "Link", @@ -943,7 +940,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "packed_items", "depends_on": "packed_items", "fieldname": "bundle_items_section", "fieldtype": "Section Break", @@ -970,22 +966,99 @@ "label": "Company Address", "read_only": 1 }, - { - "fieldname": "section_break_33", - "fieldtype": "Section Break" - }, { "fieldname": "scan_barcode", "fieldtype": "Data", "label": "Scan Barcode", "options": "Barcode" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address and Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_31", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_36", + "fieldtype": "Column Break" + }, + { + "fieldname": "billing_address_section", + "fieldtype": "Section Break", + "label": "Billing Address", + "options": "fa fa-bullhorn" + }, + { + "fieldname": "shipping_address_section", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "column_break_81", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_address_section", + "fieldtype": "Section Break", + "label": "Company Address" + }, + { + "fieldname": "column_break_87", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "depends_on": "eval:(doc.lost_reasons.length>0 || doc.order_lost_reason)", + "fieldname": "lost_reasons_section", + "fieldtype": "Section Break", + "label": "Lost Reasons" + }, + { + "fieldname": "column_break_117", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text", + "print_hide": 1 + }, + { + "fieldname": "column_break_108", + "fieldtype": "Column Break" } ], "icon": "fa fa-shopping-cart", "idx": 82, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:44:43.221804", + "modified": "2022-09-27 15:14:27.693005", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", @@ -1083,4 +1156,4 @@ "states": [], "timeline_field": "party_name", "title_field": "title" -} +} \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 5013d51a8a..c792c30249 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -847,7 +847,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_48", "fieldtype": "Section Break", "hide_days": 1, @@ -1629,7 +1628,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-09-27 12:38:57.007441", + "modified": "2022-09-27 14:51:09.128426", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 519301c304..2ac1b1c406 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -712,7 +712,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_49", "fieldtype": "Section Break", "label": "Additional Discount" @@ -1387,7 +1386,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2022-09-27 12:49:21.640004", + "modified": "2022-09-27 14:51:31.964085", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", From 10a25603ac7e318d3ce735ba0b031bde34cb4b21 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 27 Sep 2022 17:47:37 +0530 Subject: [PATCH 0328/1047] feat: Tab Break in Purchase Order --- .../purchase_order/purchase_order.json | 80 ++++++++++--------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 7f0fc57974..9d955d9a80 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -13,12 +13,12 @@ "naming_series", "supplier", "supplier_name", + "order_confirmation_no", + "order_confirmation_date", "get_items_from_open_material_requests", "column_break_7", "transaction_date", "schedule_date", - "order_confirmation_no", - "order_confirmation_date", "column_break1", "company", "apply_tds", @@ -95,26 +95,21 @@ "discount_amount", "sec_tax_breakup", "other_charges_calculation", - "contacts_and_addresses_tab", + "address_and_contact_tab", "section_addresses", "supplier_address", "address_display", + "col_break_address", "contact_person", "contact_display", "contact_mobile", "contact_email", - "col_break_address", + "company_shipping_address_section", "shipping_address", "shipping_address_display", + "company_billing_address_section", "billing_address", "billing_address_display", - "terms_tab", - "terms_section_break", - "tc_name", - "terms", - "payment_schedule_section", - "payment_terms_template", - "payment_schedule", "drop_ship", "customer", "customer_name", @@ -123,6 +118,13 @@ "customer_contact_display", "customer_contact_mobile", "customer_contact_email", + "terms_tab", + "payment_schedule_section", + "payment_terms_template", + "payment_schedule", + "terms_section_break", + "tc_name", + "terms", "more_info_tab", "tracking_section", "status", @@ -141,12 +143,12 @@ "column_break_97", "auto_repeat", "update_auto_repeat_reference", - "more_info", + "additional_info_section", + "is_internal_supplier", + "represents_company", "ref_sq", "column_break_74", "party_account_currency", - "is_internal_supplier", - "represents_company", "inter_company_order_reference", "is_old_subcontracting_flow", "dashboard" @@ -273,8 +275,9 @@ "read_only": 1 }, { + "depends_on": "eval:doc.customer", "fieldname": "drop_ship", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Drop Ship" }, { @@ -305,7 +308,6 @@ { "fieldname": "customer_contact_display", "fieldtype": "Small Text", - "hidden": 1, "label": "Customer Contact", "print_hide": 1 }, @@ -325,10 +327,9 @@ "print_hide": 1 }, { - "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Supplier Address" }, { "fieldname": "supplier_address", @@ -714,7 +715,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "apply_discount_on", "fieldname": "discount_section", "fieldtype": "Section Break", "label": "Additional Discount" @@ -857,7 +857,6 @@ "read_only": 1 }, { - "collapsible": 1, "fieldname": "payment_schedule_section", "fieldtype": "Section Break", "label": "Payment Terms" @@ -877,11 +876,10 @@ "print_hide": 1 }, { - "collapsible": 1, "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", - "label": "Terms and Conditions", + "label": "Terms & Conditions", "oldfieldtype": "Section Break", "options": "fa fa-legal" }, @@ -901,13 +899,6 @@ "oldfieldname": "terms", "oldfieldtype": "Text Editor" }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break" - }, { "default": "Draft", "fieldname": "status", @@ -1029,7 +1020,7 @@ "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section" + "label": "Auto Repeat" }, { "allow_on_submit": 1, @@ -1178,11 +1169,6 @@ "label": "Set From Warehouse", "options": "Warehouse" }, - { - "fieldname": "contacts_and_addresses_tab", - "fieldtype": "Tab Break", - "label": "Contacts & Addresses" - }, { "fieldname": "terms_tab", "fieldtype": "Tab Break", @@ -1210,13 +1196,35 @@ { "fieldname": "column_break_53", "fieldtype": "Column Break" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "company_shipping_address_section", + "fieldtype": "Section Break", + "label": "Company Shipping Address" + }, + { + "fieldname": "company_billing_address_section", + "fieldtype": "Section Break", + "label": "Company Billing Address" + }, + { + "collapsible": 1, + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info", + "oldfieldtype": "Section Break" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:45:04.954055", + "modified": "2022-09-27 17:28:35.652609", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", From 2172c5034a984d648c872629c7fb72e93bfd377d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 27 Sep 2022 17:58:43 +0530 Subject: [PATCH 0329/1047] feat: Tab Break in Material Request --- .../material_request/material_request.json | 82 +++++++++++++------ 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json index 35931307af..413c373e0e 100644 --- a/erpnext/stock/doctype/material_request/material_request.json +++ b/erpnext/stock/doctype/material_request/material_request.json @@ -11,34 +11,40 @@ "naming_series", "title", "material_request_type", - "transfer_status", "customer", - "status", + "company", "column_break_2", "transaction_date", "schedule_date", - "company", "amended_from", "warehouse_section", + "scan_barcode", + "column_break_13", "set_from_warehouse", "column_break5", "set_warehouse", "items_section", - "scan_barcode", "items", - "more_info", - "per_ordered", - "column_break2", - "per_received", - "printing_details", - "letter_head", - "select_print_heading", + "terms_tab", "terms_section_break", "tc_name", "terms", + "more_info_tab", + "status_section", + "status", + "per_ordered", + "column_break2", + "transfer_status", + "per_received", + "printing_details", + "letter_head", + "column_break_31", + "select_print_heading", "reference", "job_card", - "work_order" + "column_break_35", + "work_order", + "connections_tab" ], "fields": [ { @@ -147,14 +153,6 @@ "options": "Material Request Item", "reqd": 1 }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text" - }, { "default": "Today", "fieldname": "transaction_date", @@ -239,7 +237,6 @@ "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", - "label": "Terms and Conditions", "oldfieldtype": "Section Break", "options": "fa fa-legal" }, @@ -277,10 +274,10 @@ { "fieldname": "warehouse_section", "fieldtype": "Section Break", - "label": "Warehouse" + "hide_border": 1, + "label": "Items" }, { - "description": "Sets 'Target Warehouse' in each row of the Items table.", "fieldname": "set_warehouse", "fieldtype": "Link", "in_list_view": 1, @@ -296,7 +293,6 @@ }, { "depends_on": "eval:doc.material_request_type == 'Material Transfer'", - "description": "Sets 'Source Warehouse' in each row of the Items table.", "fieldname": "set_from_warehouse", "fieldtype": "Link", "label": "Set Source Warehouse", @@ -317,13 +313,49 @@ "label": "Work Order", "options": "Work Order", "read_only": 1 + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "collapsible": 1, + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text" + }, + { + "fieldname": "column_break_31", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_35", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" } ], "icon": "fa fa-ticket", "idx": 70, "is_submittable": 1, "links": [], - "modified": "2022-08-25 11:49:28.155048", + "modified": "2022-09-27 17:58:26.366469", "modified_by": "Administrator", "module": "Stock", "name": "Material Request", From 3d9263bf866ed3836f29d6c81cab55486e8bd4f1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 28 Sep 2022 11:34:53 +0530 Subject: [PATCH 0330/1047] feat: Tab Break in Supplier Quotation --- .../supplier_quotation.json | 118 ++++++++++-------- 1 file changed, 69 insertions(+), 49 deletions(-) diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 8d1939a101..fc853a4b82 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -13,19 +13,13 @@ "naming_series", "supplier", "supplier_name", - "column_break1", "company", + "column_break1", + "status", "transaction_date", "valid_till", "quotation_number", "amended_from", - "address_section", - "supplier_address", - "contact_person", - "address_display", - "contact_display", - "contact_mobile", - "contact_email", "currency_and_price_list", "currency", "conversion_rate", @@ -36,25 +30,23 @@ "ignore_pricing_rule", "items_section", "items", - "pricing_rule_details", - "pricing_rules", "section_break_22", "total_qty", + "total_net_weight", + "column_break_26", "base_total", "base_net_total", "column_break_24", "total", "net_total", - "total_net_weight", "taxes_section", + "taxes_and_charges", + "column_break_34", "tax_category", "column_break_36", "shipping_rule", "section_break_38", - "taxes_and_charges", "taxes", - "tax_breakup", - "other_charges_calculation", "totals", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", @@ -80,24 +72,36 @@ "rounded_total", "in_words", "disable_rounded_total", - "terms_section_break", + "tax_breakup", + "other_charges_calculation", + "pricing_rule_details", + "pricing_rules", + "address_and_contact_tab", + "supplier_address", + "address_display", + "column_break_72", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "terms_tab", "tc_name", "terms", + "more_info_tab", "printing_settings", "select_print_heading", "group_same_items", - "column_break_72", + "column_break_85", "letter_head", "language", "subscription_section", "auto_repeat", "update_auto_repeat_reference", "more_info", - "status", - "column_break_57", "is_subcontracted", - "reference", - "opportunity" + "column_break_57", + "opportunity", + "connections_tab" ], "fields": [ { @@ -146,7 +150,7 @@ "fieldname": "supplier_name", "fieldtype": "Data", "in_global_search": 1, - "label": "Name", + "label": "Supplier Name", "read_only": 1 }, { @@ -193,12 +197,6 @@ "reqd": 1, "search_index": 1 }, - { - "collapsible": 1, - "fieldname": "address_section", - "fieldtype": "Section Break", - "label": "Address and Contact" - }, { "fieldname": "supplier_address", "fieldtype": "Link", @@ -309,6 +307,8 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items", "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -316,7 +316,6 @@ "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", - "label": "Items", "oldfieldname": "po_details", "oldfieldtype": "Table", "options": "Supplier Quotation Item", @@ -394,6 +393,7 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" @@ -417,7 +417,8 @@ }, { "fieldname": "section_break_38", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "taxes_and_charges", @@ -523,7 +524,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_41", "fieldtype": "Section Break", "label": "Additional Discount" @@ -563,7 +563,8 @@ }, { "fieldname": "section_break_46", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Totals" }, { "fieldname": "base_grand_total", @@ -654,19 +655,10 @@ "fieldtype": "Check", "label": "Disable Rounded Total" }, - { - "collapsible": 1, - "collapsible_depends_on": "terms", - "fieldname": "terms_section_break", - "fieldtype": "Section Break", - "label": "Terms and Conditions", - "oldfieldtype": "Section Break", - "options": "fa fa-legal" - }, { "fieldname": "tc_name", "fieldtype": "Link", - "label": "Terms", + "label": "Terms Template", "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", @@ -729,7 +721,7 @@ { "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Auto Repeat Section" + "label": "Auto Repeat" }, { "fieldname": "auto_repeat", @@ -751,7 +743,7 @@ "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", - "label": "More Information", + "label": "Additional Info", "oldfieldtype": "Section Break", "options": "fa fa-file-text" }, @@ -779,11 +771,6 @@ "label": "Is Subcontracted", "print_hide": 1 }, - { - "fieldname": "reference", - "fieldtype": "Section Break", - "label": "Reference" - }, { "fieldname": "opportunity", "fieldtype": "Link", @@ -803,6 +790,39 @@ "fieldname": "quotation_number", "fieldtype": "Data", "label": "Quotation Number" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_34", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_85", + "fieldtype": "Column Break" } ], "icon": "fa fa-shopping-cart", @@ -810,7 +830,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-03-14 16:13:20.284572", + "modified": "2022-09-27 18:13:09.462037", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", From 35f836c4b72eaca83b7120dade976fe40ba477ff Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 28 Sep 2022 14:07:26 +0530 Subject: [PATCH 0331/1047] feat: Tab Break in Purchase Receipt --- .../purchase_receipt/purchase_receipt.json | 227 ++++++++++++------ 1 file changed, 147 insertions(+), 80 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index acaac920c9..16b51d973a 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -17,10 +17,11 @@ "supplier_name", "supplier_delivery_note", "column_break1", - "company", "posting_date", "posting_time", "set_posting_time", + "column_break_12", + "company", "apply_putaway_rule", "is_return", "return_against", @@ -28,18 +29,6 @@ "cost_center", "dimension_col_break", "project", - "section_addresses", - "supplier_address", - "contact_person", - "address_display", - "contact_display", - "contact_mobile", - "contact_email", - "col_break_address", - "shipping_address", - "shipping_address_display", - "billing_address", - "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -49,37 +38,33 @@ "plc_conversion_rate", "ignore_pricing_rule", "sec_warehouse", + "scan_barcode", + "column_break_31", "set_warehouse", - "rejected_warehouse", - "col_break_warehouse", "set_from_warehouse", + "col_break_warehouse", + "rejected_warehouse", "is_subcontracted", "supplier_warehouse", "items_section", - "scan_barcode", "items", "section_break0", "total_qty", + "total_net_weight", + "column_break_43", "base_total", "base_net_total", "column_break_27", - "total_net_weight", "total", "net_total", - "pricing_rule_details", - "pricing_rules", - "raw_material_details", - "get_current_stock", - "supplied_items", "taxes_charges_section", - "tax_category", + "taxes_and_charges", "shipping_col", + "tax_category", + "column_break_53", "shipping_rule", "taxes_section", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "totals", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", @@ -88,53 +73,81 @@ "taxes_and_charges_added", "taxes_and_charges_deducted", "total_taxes_and_charges", - "section_break_42", - "apply_discount_on", - "base_discount_amount", - "column_break_44", - "additional_discount_percentage", - "discount_amount", "section_break_46", "base_grand_total", "base_rounding_adjustment", - "base_in_words", "base_rounded_total", + "base_in_words", "column_break_50", "grand_total", "rounding_adjustment", "rounded_total", "in_words", "disable_rounded_total", - "terms_section_break", + "section_break_42", + "apply_discount_on", + "base_discount_amount", + "column_break_44", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "pricing_rule_details", + "pricing_rules", + "raw_material_details", + "get_current_stock", + "supplied_items", + "address_and_contact_tab", + "section_addresses", + "supplier_address", + "address_display", + "col_break_address", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "section_break_98", + "shipping_address", + "column_break_100", + "shipping_address_display", + "billing_address_section", + "billing_address", + "column_break_104", + "billing_address_display", + "terms_tab", "tc_name", "terms", - "more_info", + "more_info_tab", + "status_section", "status", - "amended_from", - "range", "column_break4", "per_billed", "per_returned", - "is_internal_supplier", - "inter_company_reference", - "represents_company", "subscription_detail", "auto_repeat", "printing_settings", "letter_head", "language", - "instructions", "column_break_97", "select_print_heading", - "other_details", - "remarks", "group_same_items", "transporter_info", "transporter_name", "column_break5", "lr_no", "lr_date", - "is_old_subcontracting_flow" + "additional_info_section", + "instructions", + "is_internal_supplier", + "represents_company", + "inter_company_reference", + "column_break_131", + "remarks", + "range", + "amended_from", + "is_old_subcontracting_flow", + "other_details", + "connections_tab" ], "fields": [ { @@ -223,7 +236,6 @@ "width": "100px" }, { - "description": "Time at which materials were received", "fieldname": "posting_time", "fieldtype": "Time", "label": "Posting Time", @@ -277,15 +289,14 @@ "read_only": 1 }, { - "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Supplier Address" }, { "fieldname": "supplier_address", "fieldtype": "Link", - "label": "Select Supplier Address", + "label": "Supplier Address", "options": "Address", "print_hide": 1 }, @@ -330,7 +341,7 @@ { "fieldname": "shipping_address", "fieldtype": "Link", - "label": "Select Shipping Address", + "label": "Shipping Address Template", "options": "Address", "print_hide": 1 }, @@ -410,10 +421,11 @@ }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items" }, { - "description": "Sets 'Accepted Warehouse' in each row of the items table.", "fieldname": "set_warehouse", "fieldtype": "Link", "label": "Accepted Warehouse", @@ -421,7 +433,6 @@ "print_hide": 1 }, { - "description": "Sets 'Rejected Warehouse' in each row of the items table.", "fieldname": "rejected_warehouse", "fieldtype": "Link", "label": "Rejected Warehouse", @@ -461,6 +472,7 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -578,11 +590,11 @@ "read_only": 1 }, { - "description": "Add / Edit Taxes and Charges", "fieldname": "taxes_charges_section", "fieldtype": "Section Break", - "oldfieldtype": "Section Break", - "options": "fa fa-money" + "hide_border": 1, + "label": "Taxes and Charges", + "oldfieldtype": "Section Break" }, { "fieldname": "tax_category", @@ -603,7 +615,8 @@ }, { "fieldname": "taxes_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "taxes_and_charges", @@ -709,7 +722,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_42", "fieldtype": "Section Break", "label": "Additional Discount" @@ -749,7 +761,8 @@ }, { "fieldname": "section_break_46", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Totals" }, { "fieldname": "base_grand_total", @@ -841,15 +854,6 @@ "fieldtype": "Check", "label": "Disable Rounded Total" }, - { - "collapsible": 1, - "collapsible_depends_on": "terms", - "fieldname": "terms_section_break", - "fieldtype": "Section Break", - "label": "Terms and Conditions", - "oldfieldtype": "Section Break", - "options": "fa fa-legal" - }, { "fieldname": "tc_name", "fieldtype": "Link", @@ -866,14 +870,6 @@ "oldfieldname": "terms", "oldfieldtype": "Text Editor" }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text" - }, { "default": "Draft", "fieldname": "status", @@ -941,7 +937,7 @@ { "fieldname": "subscription_detail", "fieldtype": "Section Break", - "label": "Auto Repeat Detail" + "label": "Auto Repeat" }, { "fieldname": "auto_repeat", @@ -1025,7 +1021,7 @@ "collapsible_depends_on": "transporter_name", "fieldname": "transporter_info", "fieldtype": "Section Break", - "label": "Transporter Details", + "label": "Transporter", "options": "fa fa-truck" }, { @@ -1087,7 +1083,7 @@ { "fieldname": "billing_address", "fieldtype": "Link", - "label": "Select Billing Address", + "label": "Billing Address", "options": "Address" }, { @@ -1113,7 +1109,6 @@ }, { "depends_on": "eval: doc.is_internal_supplier", - "description": "Sets 'From Warehouse' in each row of the items table.", "fieldname": "set_from_warehouse", "fieldtype": "Link", "label": "Set From Warehouse", @@ -1151,13 +1146,85 @@ "hidden": 1, "label": "Is Old Subcontracting Flow", "read_only": 1 + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_31", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_43", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_53", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_98", + "fieldtype": "Section Break", + "label": "Company Shipping Address" + }, + { + "fieldname": "billing_address_section", + "fieldtype": "Section Break", + "label": "Company Billing Address" + }, + { + "collapsible": 1, + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text" + }, + { + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info" + }, + { + "fieldname": "column_break_131", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_100", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_104", + "fieldtype": "Column Break" } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:45:58.430132", + "modified": "2022-09-28 13:07:37.482663", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", From aaf3c2b329984e6aa2c0ceacd48ac37b6da85310 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 28 Sep 2022 15:44:58 +0530 Subject: [PATCH 0332/1047] feat: Tab Break in Purchase Invoice --- .../purchase_invoice/purchase_invoice.json | 285 +++++++++++------- 1 file changed, 182 insertions(+), 103 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 1e477776e2..d994262dff 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -12,39 +12,23 @@ "supplier", "supplier_name", "tax_id", - "due_date", - "tax_withholding_category", - "column_break1", "company", + "column_break_6", "posting_date", "posting_time", "set_posting_time", + "due_date", + "column_break1", "is_paid", "is_return", + "return_against", "apply_tds", + "tax_withholding_category", "amended_from", "accounting_dimensions_section", "cost_center", "dimension_col_break", "project", - "supplier_invoice_details", - "bill_no", - "column_break_15", - "bill_date", - "returns", - "return_against", - "section_addresses", - "supplier_address", - "address_display", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "col_break_address", - "shipping_address", - "shipping_address_display", - "billing_address", - "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -54,39 +38,34 @@ "plc_conversion_rate", "ignore_pricing_rule", "sec_warehouse", - "set_warehouse", - "rejected_warehouse", - "col_break_warehouse", - "set_from_warehouse", - "supplier_warehouse", - "is_subcontracted", - "items_section", - "update_stock", "scan_barcode", + "update_stock", + "column_break_38", + "set_warehouse", + "set_from_warehouse", + "col_break_warehouse", + "rejected_warehouse", + "is_subcontracted", + "supplier_warehouse", + "items_section", "items", - "pricing_rule_details", - "pricing_rules", - "raw_materials_supplied", - "supplied_items", "section_break_26", "total_qty", + "total_net_weight", + "column_break_50", "base_total", "base_net_total", "column_break_28", - "total_net_weight", "total", "net_total", "taxes_section", + "taxes_and_charges", + "column_break_58", "tax_category", "column_break_49", "shipping_rule", "section_break_51", - "taxes_and_charges", "taxes", - "tax_withheld_vouchers_section", - "tax_withheld_vouchers", - "sec_tax_breakup", - "other_charges_calculation", "totals", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", @@ -95,12 +74,6 @@ "taxes_and_charges_added", "taxes_and_charges_deducted", "total_taxes_and_charges", - "section_break_44", - "apply_discount_on", - "base_discount_amount", - "column_break_46", - "additional_discount_percentage", - "discount_amount", "section_break_49", "base_grand_total", "base_rounding_adjustment", @@ -114,24 +87,58 @@ "total_advance", "outstanding_amount", "disable_rounded_total", + "section_break_44", + "apply_discount_on", + "base_discount_amount", + "additional_discount_account", + "column_break_46", + "additional_discount_percentage", + "discount_amount", + "tax_withheld_vouchers_section", + "tax_withheld_vouchers", + "sec_tax_breakup", + "other_charges_calculation", + "pricing_rule_details", + "pricing_rules", + "raw_materials_supplied", + "supplied_items", + "payments_tab", "payments_section", "mode_of_payment", - "cash_bank_account", + "base_paid_amount", "clearance_date", "col_br_payments", + "cash_bank_account", "paid_amount", - "base_paid_amount", + "advances_section", + "allocate_advances_automatically", + "get_advances", + "advances", + "advance_tax", "write_off", "write_off_amount", "base_write_off_amount", "column_break_61", "write_off_account", "write_off_cost_center", - "advances_section", - "allocate_advances_automatically", - "get_advances", - "advances", - "advance_tax", + "address_and_contact_tab", + "section_addresses", + "supplier_address", + "address_display", + "col_break_address", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "company_shipping_address_section", + "shipping_address", + "column_break_126", + "shipping_address_display", + "company_billing_address_section", + "billing_address", + "column_break_130", + "billing_address_display", + "terms_tab", "payment_schedule_section", "payment_terms_template", "ignore_default_payment_terms_template", @@ -139,6 +146,28 @@ "terms_section_break", "tc_name", "terms", + "more_info_tab", + "status_section", + "status", + "column_break_177", + "per_received", + "supplier_invoice_details", + "bill_no", + "column_break_15", + "bill_date", + "accounting_details_section", + "credit_to", + "party_account_currency", + "is_opening", + "against_expense_account", + "column_break_63", + "unrealized_profit_loss_account", + "subscription_section", + "auto_repeat", + "update_auto_repeat_reference", + "column_break_114", + "from_date", + "to_date", "printing_settings", "letter_head", "select_print_heading", @@ -150,28 +179,14 @@ "release_date", "cb_17", "hold_comment", - "more_info", - "status", - "inter_company_invoice_reference", + "additional_info_section", + "is_internal_supplier", "represents_company", "column_break_147", - "is_internal_supplier", - "accounting_details_section", - "credit_to", - "party_account_currency", - "is_opening", - "against_expense_account", - "column_break_63", - "unrealized_profit_loss_account", + "inter_company_invoice_reference", + "is_old_subcontracting_flow", "remarks", - "subscription_section", - "from_date", - "to_date", - "column_break_114", - "auto_repeat", - "update_auto_repeat_reference", - "per_received", - "is_old_subcontracting_flow" + "connections_tab" ], "fields": [ { @@ -354,7 +369,7 @@ "collapsible_depends_on": "bill_no", "fieldname": "supplier_invoice_details", "fieldtype": "Section Break", - "label": "Supplier Invoice Details" + "label": "Supplier Invoice" }, { "fieldname": "bill_no", @@ -377,12 +392,6 @@ "oldfieldtype": "Date", "print_hide": 1 }, - { - "depends_on": "return_against", - "fieldname": "returns", - "fieldtype": "Section Break", - "label": "Returns" - }, { "depends_on": "return_against", "fieldname": "return_against", @@ -394,10 +403,9 @@ "read_only": 1 }, { - "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Supplier Address" }, { "fieldname": "supplier_address", @@ -518,11 +526,12 @@ }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items" }, { "depends_on": "update_stock", - "description": "Sets 'Accepted Warehouse' in each row of the items table.", "fieldname": "set_warehouse", "fieldtype": "Link", "label": "Set Accepted Warehouse", @@ -531,7 +540,6 @@ }, { "depends_on": "update_stock", - "description": "Warehouse where you are maintaining stock of rejected items", "fieldname": "rejected_warehouse", "fieldtype": "Link", "label": "Rejected Warehouse", @@ -554,6 +562,7 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -581,6 +590,7 @@ "reqd": 1 }, { + "collapsible": 1, "fieldname": "pricing_rule_details", "fieldtype": "Section Break", "label": "Pricing Rules" @@ -593,6 +603,7 @@ "read_only": 1 }, { + "collapsible": 1, "collapsible_depends_on": "supplied_items", "fieldname": "raw_materials_supplied", "fieldtype": "Section Break", @@ -665,6 +676,8 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -688,7 +701,8 @@ }, { "fieldname": "section_break_51", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "taxes_and_charges", @@ -832,7 +846,8 @@ }, { "fieldname": "section_break_49", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Totals" }, { "fieldname": "base_grand_total", @@ -1003,8 +1018,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "write_off_amount", - "depends_on": "grand_total", "fieldname": "write_off", "fieldtype": "Section Break", "label": "Write Off" @@ -1081,7 +1094,6 @@ "print_hide": 1 }, { - "collapsible": 1, "collapsible_depends_on": "eval:(!doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", @@ -1102,8 +1114,6 @@ "print_hide": 1 }, { - "collapsible": 1, - "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", "label": "Terms and Conditions", @@ -1119,13 +1129,13 @@ { "fieldname": "terms", "fieldtype": "Text Editor", - "label": "Terms and Conditions1" + "label": "Terms and Conditions" }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings" + "label": "Print Settings" }, { "allow_on_submit": 1, @@ -1166,15 +1176,6 @@ "print_hide": 1, "read_only": 1 }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "print_hide": 1 - }, { "default": "0", "fetch_from": "supplier.is_internal_supplier", @@ -1260,7 +1261,7 @@ "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section", + "label": "Subscription", "print_hide": 1 }, { @@ -1356,7 +1357,6 @@ }, { "depends_on": "eval:doc.update_stock && doc.is_internal_supplier", - "description": "Sets 'From Warehouse' in each row of the items table.", "fieldname": "set_from_warehouse", "fieldtype": "Link", "label": "Set From Warehouse", @@ -1422,6 +1422,7 @@ "read_only": 1 }, { + "collapsible_depends_on": "tax_withheld_vouchers", "fieldname": "tax_withheld_vouchers_section", "fieldtype": "Section Break", "label": "Tax Withheld Vouchers" @@ -1433,6 +1434,84 @@ "no_copy": 1, "options": "Tax Withheld Vouchers", "read_only": 1 + }, + { + "fieldname": "payments_tab", + "fieldtype": "Tab Break", + "label": "Payments" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address and Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_38", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_50", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_58", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_shipping_address_section", + "fieldtype": "Section Break", + "label": "Company Shipping Address" + }, + { + "fieldname": "column_break_126", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_billing_address_section", + "fieldtype": "Section Break", + "label": "Company Billing Address" + }, + { + "fieldname": "column_break_130", + "fieldtype": "Column Break" + }, + { + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status" + }, + { + "fieldname": "column_break_177", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text", + "print_hide": 1 } ], "icon": "fa fa-file-text", From 5389a357986a323714f718b242a93729d6eebe9a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 28 Sep 2022 18:56:29 +0530 Subject: [PATCH 0333/1047] fix: more fields reordering related to Tab Break --- .../purchase_invoice/purchase_invoice.json | 7 ++++--- .../doctype/sales_invoice/sales_invoice.json | 17 +++++++--------- .../purchase_order/purchase_order.json | 18 +++++++++++++---- .../supplier_quotation.json | 6 +++--- .../regional/united_arab_emirates/setup.py | 2 +- .../selling/doctype/quotation/quotation.json | 20 +++++++++---------- .../doctype/sales_order/sales_order.json | 4 ++-- .../doctype/delivery_note/delivery_note.json | 8 ++++---- .../purchase_receipt/purchase_receipt.json | 6 +++--- 9 files changed, 48 insertions(+), 40 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index d994262dff..69e2f96000 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -170,9 +170,9 @@ "to_date", "printing_settings", "letter_head", - "select_print_heading", - "column_break_112", "group_same_items", + "column_break_112", + "select_print_heading", "language", "sb_14", "on_hold", @@ -1443,7 +1443,7 @@ { "fieldname": "address_and_contact_tab", "fieldtype": "Tab Break", - "label": "Address and Contact" + "label": "Address & Contact" }, { "fieldname": "terms_tab", @@ -1496,6 +1496,7 @@ "fieldtype": "Column Break" }, { + "collapsible": 1, "fieldname": "status_section", "fieldtype": "Section Break", "label": "Status" diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index d8b413d9ff..4353c6f41e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -12,14 +12,14 @@ "customer", "customer_name", "tax_id", + "company", + "company_tax_id", "column_break1", "posting_date", "posting_time", "set_posting_time", "due_date", "column_break_14", - "company", - "company_tax_id", "is_pos", "pos_profile", "is_consolidated", @@ -87,8 +87,8 @@ "disable_rounded_total", "section_break_49", "apply_discount_on", - "is_cash_or_non_trade_discount", "base_discount_amount", + "is_cash_or_non_trade_discount", "additional_discount_account", "column_break_51", "additional_discount_percentage", @@ -1055,7 +1055,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_49", "fieldtype": "Section Break", "hide_days": 1, @@ -1111,6 +1110,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, + "label": "Totals", "oldfieldtype": "Section Break", "options": "fa fa-money", "print_hide": 1 @@ -1292,8 +1292,6 @@ "print_hide": 1 }, { - "collapsible": 1, - "collapsible_depends_on": "eval:(!doc.is_pos && !doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", "hide_days": 1, @@ -1325,7 +1323,7 @@ { "collapsible": 1, "collapsible_depends_on": "eval:!doc.is_pos", - "depends_on": "eval:doc.is_pos===1||(doc.advances && doc.advances.length>0)", + "depends_on": "eval:doc.is_pos===1", "fieldname": "payments_section", "fieldtype": "Section Break", "hide_days": 1, @@ -1363,6 +1361,7 @@ "hide_seconds": 1 }, { + "depends_on": "eval: doc.is_pos || doc.redeem_loyalty_points", "fieldname": "base_paid_amount", "fieldtype": "Currency", "hide_days": 1, @@ -1498,8 +1497,6 @@ "print_hide": 1 }, { - "collapsible": 1, - "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", "hide_days": 1, @@ -2111,7 +2108,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2022-09-27 14:15:35.293825", + "modified": "2022-09-28 19:23:38.265409", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 9d955d9a80..a2f96375f5 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -106,9 +106,11 @@ "contact_email", "company_shipping_address_section", "shipping_address", + "column_break_99", "shipping_address_display", "company_billing_address_section", "billing_address", + "column_break_103", "billing_address_display", "drop_ship", "customer", @@ -133,10 +135,10 @@ "per_received", "column_break5", "letter_head", - "select_print_heading", - "column_break_86", - "language", "group_same_items", + "column_break_86", + "select_print_heading", + "language", "subscription_section", "from_date", "to_date", @@ -1218,13 +1220,21 @@ "fieldtype": "Section Break", "label": "Additional Info", "oldfieldtype": "Section Break" + }, + { + "fieldname": "column_break_99", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_103", + "fieldtype": "Column Break" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-09-27 17:28:35.652609", + "modified": "2022-09-28 18:20:49.494279", "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 fc853a4b82..16365618ca 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -89,10 +89,10 @@ "terms", "more_info_tab", "printing_settings", - "select_print_heading", + "letter_head", "group_same_items", "column_break_85", - "letter_head", + "select_print_heading", "language", "subscription_section", "auto_repeat", @@ -830,7 +830,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-09-27 18:13:09.462037", + "modified": "2022-09-27 18:20:09.462037", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index be621bcdd1..36a079546e 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -37,7 +37,7 @@ def make_custom_fields(): fieldname="vat_section", label="VAT Details", fieldtype="Section Break", - insert_after="group_same_items", + insert_after="language", print_hide=1, collapsible=1, ), diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 78b739cedb..f858e26fcb 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -103,15 +103,20 @@ "tc_name", "terms", "more_info_tab", + "subscription_section", + "auto_repeat", + "update_auto_repeat_reference", "print_settings", "letter_head", "group_same_items", "column_break_73", "select_print_heading", "language", - "subscription_section", - "auto_repeat", - "update_auto_repeat_reference", + "lost_reasons_section", + "lost_reasons", + "competitors", + "column_break_117", + "order_lost_reason", "additional_info_section", "status", "customer_group", @@ -123,11 +128,6 @@ "opportunity", "supplier_quotation", "enq_det", - "lost_reasons_section", - "lost_reasons", - "competitors", - "column_break_117", - "order_lost_reason", "connections_tab" ], "fields": [ @@ -975,7 +975,7 @@ { "fieldname": "address_and_contact_tab", "fieldtype": "Tab Break", - "label": "Address and Contact" + "label": "Address & Contact" }, { "fieldname": "terms_tab", @@ -1058,7 +1058,7 @@ "idx": 82, "is_submittable": 1, "links": [], - "modified": "2022-09-27 15:14:27.693005", + "modified": "2022-09-27 15:18:27.693005", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index c792c30249..1f3af0cc0f 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -151,11 +151,11 @@ "auto_repeat", "update_auto_repeat_reference", "printing_details", - "language", "letter_head", "group_same_items", "column_break4", "select_print_heading", + "language", "additional_info_section", "is_internal_customer", "represents_company", @@ -1628,7 +1628,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-09-27 14:51:09.128426", + "modified": "2022-09-28 18:30:34.723896", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 2ac1b1c406..541f96b192 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -149,11 +149,11 @@ "auto_repeat", "printing_details", "letter_head", - "select_print_heading", - "column_break_88", - "language", "print_without_amount", "group_same_items", + "column_break_88", + "select_print_heading", + "language", "more_info", "is_internal_customer", "represents_company", @@ -1386,7 +1386,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2022-09-27 14:51:31.964085", + "modified": "2022-09-27 14:55:31.964085", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 16b51d973a..426b509b9e 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -127,10 +127,10 @@ "auto_repeat", "printing_settings", "letter_head", - "language", + "group_same_items", "column_break_97", "select_print_heading", - "group_same_items", + "language", "transporter_info", "transporter_name", "column_break5", @@ -1224,7 +1224,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2022-09-28 13:07:37.482663", + "modified": "2022-09-28 13:09:37.482663", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", From e71c417f7e5dd6015b58c770b8c3d1dcca7d70bf Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 11 Oct 2022 13:17:13 +0530 Subject: [PATCH 0334/1047] fix: minor cleanup --- .../doctype/purchase_invoice/purchase_invoice.json | 14 +++++++------- .../doctype/sales_invoice/sales_invoice.json | 3 ++- .../doctype/purchase_order/purchase_order.json | 6 ++---- erpnext/selling/doctype/quotation/quotation.json | 14 +++++++------- .../selling/doctype/sales_order/sales_order.json | 5 +++-- .../stock/doctype/delivery_note/delivery_note.json | 7 ++++--- .../doctype/purchase_receipt/purchase_receipt.json | 4 ++-- 7 files changed, 27 insertions(+), 26 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 69e2f96000..e73d602332 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -39,13 +39,12 @@ "ignore_pricing_rule", "sec_warehouse", "scan_barcode", + "col_break_warehouse", "update_stock", - "column_break_38", "set_warehouse", "set_from_warehouse", - "col_break_warehouse", - "rejected_warehouse", "is_subcontracted", + "rejected_warehouse", "supplier_warehouse", "items_section", "items", @@ -186,7 +185,8 @@ "inter_company_invoice_reference", "is_old_subcontracting_flow", "remarks", - "connections_tab" + "connections_tab", + "column_break_38" ], "fields": [ { @@ -667,6 +667,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -806,7 +807,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_44", "fieldtype": "Section Break", "label": "Additional Discount" @@ -1340,7 +1340,7 @@ }, { "depends_on": "eval:doc.is_internal_supplier", - "description": "Unrealized Profit / Loss account for intra-company transfers", + "description": "Unrealized Profit/Loss account for intra-company transfers", "fieldname": "unrealized_profit_loss_account", "fieldtype": "Link", "label": "Unrealized Profit / Loss Account", @@ -1519,7 +1519,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2022-10-07 14:19:14.214157", + "modified": "2022-10-11 13:04:44.304389", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 4353c6f41e..97e5f4017e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -852,6 +852,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "hide_days": 1, @@ -2108,7 +2109,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2022-09-28 19:23:38.265409", + "modified": "2022-10-11 13:07:36.488095", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index a2f96375f5..2193985ff2 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -454,7 +454,6 @@ "print_hide": 1 }, { - "description": "Sets 'Warehouse' in each row of the Items table.", "fieldname": "set_warehouse", "fieldtype": "Link", "label": "Set Target Warehouse", @@ -578,6 +577,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -707,7 +707,6 @@ "read_only": 1 }, { - "depends_on": "total_taxes_and_charges", "fieldname": "total_taxes_and_charges", "fieldtype": "Currency", "label": "Total Taxes and Charges", @@ -781,7 +780,6 @@ "read_only": 1 }, { - "description": "In Words will be visible once you save the Purchase Order.", "fieldname": "base_in_words", "fieldtype": "Data", "label": "In Words (Company Currency)", @@ -1234,7 +1232,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-09-28 18:20:49.494279", + "modified": "2022-10-11 13:01:41.674352", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index f858e26fcb..fa64b1625b 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -301,7 +301,7 @@ "read_only": 1 }, { - "depends_on": "eval:doc.quotation_to=='Customer' && doc.party_name", + "depends_on": "eval:(doc.quotation_to=='Customer' && doc.party_name)", "fieldname": "col_break98", "fieldtype": "Column Break", "width": "50%" @@ -321,7 +321,7 @@ "read_only": 1 }, { - "depends_on": "eval:doc.quotation_to=='Customer' && doc.party_name", + "depends_on": "eval:(doc.quotation_to=='Customer' && doc.party_name)", "fieldname": "customer_group", "fieldtype": "Link", "hidden": 1, @@ -490,6 +490,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -843,7 +844,7 @@ }, { "allow_on_submit": 1, - "depends_on": "eval: doc.auto_repeat", + "depends_on": "eval:doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", "label": "Update Auto Repeat Reference" @@ -868,7 +869,7 @@ }, { "allow_on_submit": 1, - "depends_on": "eval:doc.status===\"Lost\"", + "depends_on": "eval:doc.status=='Lost'", "fieldname": "order_lost_reason", "fieldtype": "Small Text", "label": "Detailed Reason", @@ -931,7 +932,6 @@ "read_only": 1 }, { - "depends_on": "packed_items", "fieldname": "packed_items", "fieldtype": "Table", "label": "Bundle Items", @@ -1031,7 +1031,7 @@ }, { "collapsible": 1, - "depends_on": "eval:(doc.lost_reasons.length>0 || doc.order_lost_reason)", + "depends_on": "eval:(doc.lost_reasons || doc.order_lost_reason)", "fieldname": "lost_reasons_section", "fieldtype": "Section Break", "label": "Lost Reasons" @@ -1058,7 +1058,7 @@ "idx": 82, "is_submittable": 1, "links": [], - "modified": "2022-09-27 15:18:27.693005", + "modified": "2022-10-11 13:06:33.479650", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 1f3af0cc0f..e6ff39d8d4 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -82,9 +82,9 @@ "advance_paid", "disable_rounded_total", "section_break_48", - "coupon_code", "apply_discount_on", "base_discount_amount", + "coupon_code", "column_break_50", "additional_discount_percentage", "discount_amount", @@ -693,6 +693,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "hide_days": 1, @@ -1628,7 +1629,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-09-28 18:30:34.723896", + "modified": "2022-10-11 13:06:10.469796", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 541f96b192..0ca3e69f76 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -41,7 +41,6 @@ "ignore_pricing_rule", "items_section", "scan_barcode", - "column_break_25", "pick_list", "col_break_warehouse", "set_warehouse", @@ -163,7 +162,8 @@ "column_break5", "excise_page", "instructions", - "connections_tab" + "connections_tab", + "column_break_25" ], "fields": [ { @@ -613,6 +613,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -1386,7 +1387,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2022-09-27 14:55:31.964085", + "modified": "2022-10-11 13:06:58.655635", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 426b509b9e..3141212ee5 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -583,6 +583,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -920,7 +921,6 @@ "width": "50%" }, { - "description": "Track this Purchase Receipt against any Project", "fieldname": "project", "fieldtype": "Link", "label": "Project", @@ -1224,7 +1224,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2022-09-28 13:09:37.482663", + "modified": "2022-10-11 13:02:31.776256", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", From b31c3bd35db39a1abdbb30aaebc017a833a5a61f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 11 Oct 2022 14:53:26 +0530 Subject: [PATCH 0335/1047] chore: Increase incoming_rate field precision to 6 --- .../doctype/sales_invoice_item/sales_invoice_item.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 4f97b63789..a307a6c17c 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -820,6 +820,7 @@ "label": "Incoming Rate (Costing)", "no_copy": 1, "options": "Company:company:default_currency", + "precision": "6", "print_hide": 1 }, { @@ -875,7 +876,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-09-06 14:17:43.394309", + "modified": "2022-10-10 20:57:38.340026", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", From dc20b21fb5162c7905662c10d954a07b4c30ca54 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 11 Oct 2022 14:54:27 +0530 Subject: [PATCH 0336/1047] test: Internal tranfer precision loss test --- .../sales_invoice/test_sales_invoice.py | 214 +++++++++++------- erpnext/stock/doctype/item/test_item.py | 7 +- .../test_stock_ledger_entry.py | 18 +- .../stock_reconciliation.py | 4 +- 4 files changed, 147 insertions(+), 96 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index ce44ae304b..b63b9edab0 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -32,10 +32,20 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import ( get_qty_after_transaction, make_stock_entry, ) -from erpnext.stock.utils import get_incoming_rate +from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( + create_stock_reconciliation, +) +from erpnext.stock.utils import get_incoming_rate, get_stock_balance class TestSalesInvoice(unittest.TestCase): + def setUp(self): + from erpnext.stock.doctype.stock_ledger_entry.test_stock_ledger_entry import create_items + + create_items(["_Test Internal Transfer Item"], uoms=[{"uom": "Box", "conversion_factor": 10}]) + create_internal_parties() + setup_accounts() + def make(self): w = frappe.copy_doc(test_records[0]) w.is_pos = 0 @@ -1705,7 +1715,7 @@ class TestSalesInvoice(unittest.TestCase): si.save() self.assertEqual(si.get("items")[0].rate, flt((price_list_rate * 25) / 100 + price_list_rate)) - def test_outstanding_amount_after_advance_jv_cancelation(self): + def test_outstanding_amount_after_advance_jv_cancellation(self): from erpnext.accounts.doctype.journal_entry.test_journal_entry import ( test_records as jv_test_records, ) @@ -1749,7 +1759,7 @@ class TestSalesInvoice(unittest.TestCase): flt(si.rounded_total + si.total_advance, si.precision("outstanding_amount")), ) - def test_outstanding_amount_after_advance_payment_entry_cancelation(self): + def test_outstanding_amount_after_advance_payment_entry_cancellation(self): pe = frappe.get_doc( { "doctype": "Payment Entry", @@ -2367,29 +2377,6 @@ class TestSalesInvoice(unittest.TestCase): acc_settings.save() def test_inter_company_transaction(self): - from erpnext.selling.doctype.customer.test_customer import create_internal_customer - - create_internal_customer( - customer_name="_Test Internal Customer", - represents_company="_Test Company 1", - allowed_to_interact_with="Wind Power LLC", - ) - - if not frappe.db.exists("Supplier", "_Test Internal Supplier"): - supplier = frappe.get_doc( - { - "supplier_group": "_Test Supplier Group", - "supplier_name": "_Test Internal Supplier", - "doctype": "Supplier", - "is_internal_supplier": 1, - "represents_company": "Wind Power LLC", - } - ) - - supplier.append("companies", {"company": "_Test Company 1"}) - - supplier.insert() - si = create_sales_invoice( company="Wind Power LLC", customer="_Test Internal Customer", @@ -2440,38 +2427,6 @@ class TestSalesInvoice(unittest.TestCase): "Expenses Included In Valuation - _TC1", ) - if not frappe.db.exists("Customer", "_Test Internal Customer"): - customer = frappe.get_doc( - { - "customer_group": "_Test Customer Group", - "customer_name": "_Test Internal Customer", - "customer_type": "Individual", - "doctype": "Customer", - "territory": "_Test Territory", - "is_internal_customer": 1, - "represents_company": "_Test Company 1", - } - ) - - customer.append("companies", {"company": "Wind Power LLC"}) - - customer.insert() - - if not frappe.db.exists("Supplier", "_Test Internal Supplier"): - supplier = frappe.get_doc( - { - "supplier_group": "_Test Supplier Group", - "supplier_name": "_Test Internal Supplier", - "doctype": "Supplier", - "is_internal_supplier": 1, - "represents_company": "Wind Power LLC", - } - ) - - supplier.append("companies", {"company": "_Test Company 1"}) - - supplier.insert() - # begin test si = create_sales_invoice( company="Wind Power LLC", @@ -2541,34 +2496,9 @@ class TestSalesInvoice(unittest.TestCase): se.cancel() def test_internal_transfer_gl_entry(self): - ## Create internal transfer account - from erpnext.selling.doctype.customer.test_customer import create_internal_customer - - account = create_account( - account_name="Unrealized Profit", - parent_account="Current Liabilities - TCP1", - company="_Test Company with perpetual inventory", - ) - - frappe.db.set_value( - "Company", "_Test Company with perpetual inventory", "unrealized_profit_loss_account", account - ) - - customer = create_internal_customer( - "_Test Internal Customer 2", - "_Test Company with perpetual inventory", - "_Test Company with perpetual inventory", - ) - - create_internal_supplier( - "_Test Internal Supplier 2", - "_Test Company with perpetual inventory", - "_Test Company with perpetual inventory", - ) - si = create_sales_invoice( company="_Test Company with perpetual inventory", - customer=customer, + customer="_Test Internal Customer 2", debit_to="Debtors - TCP1", warehouse="Stores - TCP1", income_account="Sales - TCP1", @@ -2582,7 +2512,7 @@ class TestSalesInvoice(unittest.TestCase): si.update_stock = 1 si.items[0].target_warehouse = "Work In Progress - TCP1" - # Add stock to stores for succesful stock transfer + # Add stock to stores for successful stock transfer make_stock_entry( target="Stores - TCP1", company="_Test Company with perpetual inventory", qty=1, basic_rate=100 ) @@ -2638,6 +2568,77 @@ class TestSalesInvoice(unittest.TestCase): check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1)) + def test_internal_transfer_gl_precision_issues(self): + # Make a stock queue of an item with two valuations + + # Remove all existing stock for this + if get_stock_balance("_Test Internal Transfer Item", "Stores - TCP1", "2022-04-10"): + create_stock_reconciliation( + item_code="_Test Internal Transfer Item", + warehouse="Stores - TCP1", + qty=0, + rate=0, + company="_Test Company with perpetual inventory", + expense_account="Stock Adjustment - TCP1" + if frappe.get_all("Stock Ledger Entry") + else "Temporary Opening - TCP1", + posting_date="2020-04-10", + posting_time="14:00", + ) + + make_stock_entry( + item_code="_Test Internal Transfer Item", + target="Stores - TCP1", + qty=9000000, + basic_rate=52.0, + posting_date="2020-04-10", + posting_time="14:00", + ) + make_stock_entry( + item_code="_Test Internal Transfer Item", + target="Stores - TCP1", + qty=60000000, + basic_rate=52.349777, + posting_date="2020-04-10", + posting_time="14:00", + ) + + # Make an internal transfer Sales Invoice Stock in non stock uom to check + # for rounding errors while converting to stock uom + si = create_sales_invoice( + company="_Test Company with perpetual inventory", + customer="_Test Internal Customer 2", + item_code="_Test Internal Transfer Item", + qty=5000000, + uom="Box", + debit_to="Debtors - TCP1", + warehouse="Stores - TCP1", + income_account="Sales - TCP1", + expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", + currency="INR", + do_not_save=1, + ) + + # Check GL Entries with precision + si.update_stock = 1 + si.items[0].target_warehouse = "Work In Progress - TCP1" + si.items[0].conversion_factor = 10 + si.save() + si.submit() + + # Check if adjustment entry is created + self.assertTrue( + frappe.db.exists( + "GL Entry", + { + "voucher_type": "Sales Invoice", + "voucher_no": si.name, + "remarks": "Rounding gain/loss Entry for Stock Transfer", + }, + ) + ) + def test_item_tax_net_range(self): item = create_item("T Shirt") @@ -3077,7 +3078,7 @@ class TestSalesInvoice(unittest.TestCase): [deferred_account, 2022.47, 0.0, "2019-03-15"], ] - gl_entries = gl_entries = frappe.db.sql( + gl_entries = frappe.db.sql( """select account, debit, credit, posting_date from `tabGL Entry` where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s @@ -3431,6 +3432,34 @@ def get_taxes_and_charges(): ] +def create_internal_parties(): + from erpnext.selling.doctype.customer.test_customer import create_internal_customer + + create_internal_customer( + customer_name="_Test Internal Customer", + represents_company="_Test Company 1", + allowed_to_interact_with="Wind Power LLC", + ) + + create_internal_customer( + customer_name="_Test Internal Customer 2", + represents_company="_Test Company with perpetual inventory", + allowed_to_interact_with="_Test Company with perpetual inventory", + ) + + create_internal_supplier( + supplier_name="_Test Internal Supplier", + represents_company="Wind Power LLC", + allowed_to_interact_with="_Test Company 1", + ) + + create_internal_supplier( + supplier_name="_Test Internal Supplier 2", + represents_company="_Test Company with perpetual inventory", + allowed_to_interact_with="_Test Company with perpetual inventory", + ) + + def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with): if not frappe.db.exists("Supplier", supplier_name): supplier = frappe.get_doc( @@ -3453,6 +3482,19 @@ def create_internal_supplier(supplier_name, represents_company, allowed_to_inter return supplier_name +def setup_accounts(): + ## Create internal transfer account + account = create_account( + account_name="Unrealized Profit", + parent_account="Current Liabilities - TCP1", + company="_Test Company with perpetual inventory", + ) + + frappe.db.set_value( + "Company", "_Test Company with perpetual inventory", "unrealized_profit_loss_account", account + ) + + def add_taxes(doc): doc.append( "taxes", diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index e35c8bf335..31c9919708 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -32,7 +32,7 @@ test_ignore = ["BOM"] test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"] -def make_item(item_code=None, properties=None): +def make_item(item_code=None, properties=None, uoms=None): if not item_code: item_code = frappe.generate_hash(length=16) @@ -56,6 +56,11 @@ def make_item(item_code=None, properties=None): for item_default in [doc for doc in item.get("item_defaults") if not doc.default_warehouse]: item_default.default_warehouse = "_Test Warehouse - _TC" item_default.company = "_Test Company" + + if uoms: + for uom in uoms: + item.append("uoms", uom) + item.insert() return item diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index 1410da56a3..6c341d9e9e 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -1323,13 +1323,15 @@ def create_product_bundle_item(new_item_code, packed_items): item.save() -def create_items(): - items = [ - "_Test Item for Reposting", - "_Test Finished Item for Reposting", - "_Test Subcontracted Item for Reposting", - "_Test Bundled Item for Reposting", - ] +def create_items(items=None, uoms=None): + if not items: + items = [ + "_Test Item for Reposting", + "_Test Finished Item for Reposting", + "_Test Subcontracted Item for Reposting", + "_Test Bundled Item for Reposting", + ] + for d in items: properties = {"valuation_method": "FIFO"} if d == "_Test Bundled Item for Reposting": @@ -1337,7 +1339,7 @@ def create_items(): elif d == "_Test Subcontracted Item for Reposting": properties.update({"is_sub_contracted_item": 1}) - make_item(d, properties=properties) + make_item(d, properties=properties, uoms=uoms) return items diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 23e0f1efaf..d92d0f1686 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -132,7 +132,9 @@ class StockReconciliation(StockController): key.append(row.get(field)) if key in item_warehouse_combinations: - self.validation_messages.append(_get_msg(row_num, _("Duplicate entry"))) + self.validation_messages.append( + _get_msg(row_num, _("Same item and warehouse combination already entered.")) + ) else: item_warehouse_combinations.append(key) From df2a0e265b85010d92b1aab50ccccc531b26c8fe Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 11 Oct 2022 14:55:09 +0530 Subject: [PATCH 0337/1047] chore: GL Entries for SLE diff --- erpnext/controllers/stock_controller.py | 29 +++++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 58cfc4fad9..4629f4911c 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -149,7 +149,7 @@ class StockController(AccountsController): if warehouse_account.get(sle.warehouse): # from warehouse account - sle_rounding_diff += flt(sle.stock_value_difference, precision) + sle_rounding_diff += flt(sle.stock_value_difference) self.check_expense_account(item_row) @@ -193,19 +193,24 @@ class StockController(AccountsController): elif sle.warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(sle.warehouse) - if sle_rounding_diff > 0: - expense_account = item_row.get("expense_account") - target_warehouse_account = warehouse_account[item_row.get("target_warehouse")]["account"] - source_warehouse_account = warehouse_account[item_row.get("warehouse")]["account"] + if abs(sle_rounding_diff) > 0.1 and ( + self.get("is_internal_customer") or self.get("is_internal_supplier") + ): + asset_account = "" + if self.get("is_internal_customer"): + asset_account = warehouse_account[item_row.get("target_warehouse")]["account"] + elif self.get("is_internal_supplier"): + asset_account = warehouse_account[item_row.get("from_warehouse")]["account"] + expense_account = item_row.get("expense_account") gl_list.append( self.get_gl_dict( { - "account": target_warehouse_account or expense_account, - "against": expense_account, + "account": expense_account, + "against": asset_account, "cost_center": item_row.cost_center, "project": item_row.project or self.get("project"), - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "remarks": _("Rounding gain/loss Entry for Stock Transfer"), "debit": sle_rounding_diff, "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No", }, @@ -217,11 +222,11 @@ class StockController(AccountsController): gl_list.append( self.get_gl_dict( { - "account": source_warehouse_account or expense_account, - "against": target_warehouse_account, + "account": asset_account, + "against": expense_account, "cost_center": item_row.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "debit": -1 * sle_rounding_diff, + "remarks": _("Rounding gain/loss Entry for Stock Transfer"), + "credit": sle_rounding_diff, "project": item_row.get("project") or self.get("project"), "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No", }, From 6ce3ce758c7a62435e3dcdfb29606848e5959cb6 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 12 Oct 2022 10:57:58 +0530 Subject: [PATCH 0338/1047] ci: disable orchestrator (#32571) --- .github/workflows/server-tests-mariadb.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml index e3b92fd269..ed731b8a31 100644 --- a/.github/workflows/server-tests-mariadb.yml +++ b/.github/workflows/server-tests-mariadb.yml @@ -120,7 +120,7 @@ jobs: FRAPPE_BRANCH: ${{ github.event.inputs.branch }} - name: Run Tests - run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator --with-coverage + run: 'cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --with-coverage --total-builds 4 --build-number ${{ matrix.container }}' env: TYPE: server CI_BUILD_ID: ${{ github.run_id }} From 7da32c7db34688120359703df135dbf302af3421 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Wed, 12 Oct 2022 12:15:07 +0530 Subject: [PATCH 0339/1047] fix: `Brand Defaults` filters --- erpnext/setup/doctype/brand/brand.js | 68 ++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/erpnext/setup/doctype/brand/brand.js b/erpnext/setup/doctype/brand/brand.js index 3680906057..0abb71a362 100644 --- a/erpnext/setup/doctype/brand/brand.js +++ b/erpnext/setup/doctype/brand/brand.js @@ -1,13 +1,71 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt +frappe.ui.form.on('Brand', { + setup: (frm) => { + frm.fields_dict["brand_defaults"].grid.get_field("default_warehouse").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + filters: { company: row.company } + } + } + frm.fields_dict["brand_defaults"].grid.get_field("default_discount_account").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + filters: { + 'report_type': 'Profit and Loss', + 'company': row.company, + "is_group": 0 + } + }; + } -//--------- ONLOAD ------------- -cur_frm.cscript.onload = function(doc, cdt, cdn) { + frm.fields_dict["brand_defaults"].grid.get_field("buying_cost_center").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + filters: { + "is_group": 0, + "company": row.company + } + } + } -} + frm.fields_dict["brand_defaults"].grid.get_field("expense_account").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + query: "erpnext.controllers.queries.get_expense_account", + filters: { company: row.company } + } + } -cur_frm.cscript.refresh = function(doc, cdt, cdn) { + frm.fields_dict["brand_defaults"].grid.get_field("default_provisional_account").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + filters: { + "company": row.company, + "root_type": ["in", ["Liability", "Asset"]], + "is_group": 0 + } + }; + } -} + frm.fields_dict["brand_defaults"].grid.get_field("selling_cost_center").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + filters: { + "is_group": 0, + "company": row.company + } + } + } + + frm.fields_dict["brand_defaults"].grid.get_field("income_account").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + query: "erpnext.controllers.queries.get_income_account", + filters: { company: row.company } + } + } + } +}); \ No newline at end of file From 43037d893d6c31e06df88883f1c529276d28803b Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 12 Oct 2022 13:07:58 +0530 Subject: [PATCH 0340/1047] fix: type-cast while saving an item (#32549) --- erpnext/stock/doctype/item/item.py | 4 ++-- erpnext/stock/doctype/item/test_item.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index c8bb1b960e..20bc9d9b2c 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -232,10 +232,10 @@ class Item(Document): def clear_retain_sample(self): if not self.has_batch_no: - self.retain_sample = None + self.retain_sample = False if not self.retain_sample: - self.sample_quantity = None + self.sample_quantity = 0 def add_default_uom_in_conversion_factor_table(self): if not self.is_new() and self.has_value_changed("stock_uom"): diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index e35c8bf335..0c710b0ef4 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -717,8 +717,8 @@ class TestItem(FrappeTestCase): item.has_batch_no = None item.save() - self.assertEqual(item.retain_sample, None) - self.assertEqual(item.sample_quantity, None) + self.assertEqual(item.retain_sample, False) + self.assertEqual(item.sample_quantity, 0) item.delete() def consume_item_code_with_differet_stock_transactions( From c8d2181498616c8c6757e28255977c66a65a30d9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 12 Oct 2022 14:17:55 +0530 Subject: [PATCH 0341/1047] chore: Increase precision for other doc fields --- .../doctype/purchase_invoice_item/purchase_invoice_item.json | 3 ++- .../stock/doctype/delivery_note_item/delivery_note_item.json | 3 ++- .../doctype/purchase_receipt_item/purchase_receipt_item.json | 3 ++- 3 files changed, 6 insertions(+), 3 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 fca7e3a887..9de9036887 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -711,6 +711,7 @@ "label": "Valuation Rate", "no_copy": 1, "options": "Company:company:default_currency", + "precision": "6", "print_hide": 1, "read_only": 1 }, @@ -870,7 +871,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-09-27 10:54:23.980713", + "modified": "2022-10-12 03:37:29.032732", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 0911cdb476..0a5cbabab0 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -753,6 +753,7 @@ "fieldtype": "Currency", "label": "Incoming Rate", "no_copy": 1, + "precision": "6", "print_hide": 1, "read_only": 1 }, @@ -813,7 +814,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-09-06 14:19:42.876357", + "modified": "2022-10-12 03:36:05.344847", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 39833b5e91..772736e0ad 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -744,6 +744,7 @@ "oldfieldname": "valuation_rate", "oldfieldtype": "Currency", "options": "Company:company:default_currency", + "precision": "6", "print_hide": 1, "print_width": "80px", "read_only": 1, @@ -999,7 +1000,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-07-28 19:27:54.880781", + "modified": "2022-10-12 03:37:59.516609", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From 1c05c004cd1739ecdf724a87edd2472991c16cfe Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 12 Oct 2022 14:19:09 +0530 Subject: [PATCH 0342/1047] chore: Use proper accounts --- erpnext/controllers/stock_controller.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 4629f4911c..b32f71750f 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -193,21 +193,22 @@ class StockController(AccountsController): elif sle.warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(sle.warehouse) - if abs(sle_rounding_diff) > 0.1 and ( + if abs(sle_rounding_diff) < (1.0 / (10**precision)) and ( self.get("is_internal_customer") or self.get("is_internal_supplier") ): - asset_account = "" + warehouse_asset_account = "" if self.get("is_internal_customer"): - asset_account = warehouse_account[item_row.get("target_warehouse")]["account"] + warehouse_asset_account = warehouse_account[item_row.get("target_warehouse")]["account"] elif self.get("is_internal_supplier"): - asset_account = warehouse_account[item_row.get("from_warehouse")]["account"] + warehouse_asset_account = warehouse_account[item_row.get("warehouse")]["account"] + + expense_account = frappe.db.get_value("Company", self.company, "default_expense_account") - expense_account = item_row.get("expense_account") gl_list.append( self.get_gl_dict( { "account": expense_account, - "against": asset_account, + "against": warehouse_asset_account, "cost_center": item_row.cost_center, "project": item_row.project or self.get("project"), "remarks": _("Rounding gain/loss Entry for Stock Transfer"), @@ -222,7 +223,7 @@ class StockController(AccountsController): gl_list.append( self.get_gl_dict( { - "account": asset_account, + "account": warehouse_asset_account, "against": expense_account, "cost_center": item_row.cost_center, "remarks": _("Rounding gain/loss Entry for Stock Transfer"), From 49601558c6f10beafa64372dc3203645259d9264 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 12 Oct 2022 14:57:16 +0530 Subject: [PATCH 0343/1047] chore: fix precision condition --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index b32f71750f..31c480f98d 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -193,7 +193,7 @@ class StockController(AccountsController): elif sle.warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(sle.warehouse) - if abs(sle_rounding_diff) < (1.0 / (10**precision)) and ( + if abs(sle_rounding_diff) > (1.0 / (10**precision)) and ( self.get("is_internal_customer") or self.get("is_internal_supplier") ): warehouse_asset_account = "" From 65992304bc259a44c60198b3b6e3be0f5b107b3e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 4 Oct 2022 16:17:56 +0530 Subject: [PATCH 0344/1047] fix: delete old ple's on item value repost --- erpnext/accounts/utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 95ba3d86ce..02dcd683fa 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1171,6 +1171,10 @@ def _delete_gl_entries(voucher_type, voucher_no): where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no), ) + ple = qb.DocType("Payment Ledger Entry") + qb.from_(ple).delete().where( + (ple.voucher_type == voucher_type) & (ple.voucher_no == voucher_no) + ).run() def sort_stock_vouchers_by_posting_date( From 683a47f7a1676aced82a2abab086510228fcc7b4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 11 Oct 2022 15:11:39 +0530 Subject: [PATCH 0345/1047] fix: consider sales rate as incoming rate for transit warehouse in purchase flow --- .../purchase_invoice/purchase_invoice.py | 6 +- .../sales_invoice/test_sales_invoice.py | 1 + .../purchase_receipt/test_purchase_receipt.py | 234 ++++++++++++++++++ erpnext/stock/stock_ledger.py | 62 +++-- 4 files changed, 278 insertions(+), 25 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 2b633cb8c3..5dbe7ebc86 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -705,6 +705,10 @@ class PurchaseInvoice(BuyingController): ) ) + credit_amount = item.base_net_amount + if self.is_internal_supplier and item.valuation_rate: + credit_amount = flt(item.valuation_rate * item.stock_qty) + # Intentionally passed negative debit amount to avoid incorrect GL Entry validation gl_entries.append( self.get_gl_dict( @@ -714,7 +718,7 @@ class PurchaseInvoice(BuyingController): "cost_center": item.cost_center, "project": item.project or self.project, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")), + "debit": -1 * flt(credit_amount, item.precision("base_net_amount")), }, warehouse_account[item.from_warehouse]["account_currency"], item=item, diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index ce44ae304b..b548ea3c00 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3306,6 +3306,7 @@ def create_sales_invoice(**args): "item_name": args.item_name or "_Test Item", "description": args.description or "_Test Item", "warehouse": args.warehouse or "_Test Warehouse - _TC", + "target_warehouse": args.target_warehouse or "_Test Warehouse 1 - _TC", "qty": args.qty or 1, "uom": args.uom or "Nos", "stock_uom": args.uom or "Nos", diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 62697244ba..1f807b8249 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2,6 +2,9 @@ # License: GNU General Public License v3. See license.txt +from cmath import pi +from turtle import update + import frappe from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, cint, cstr, flt, today @@ -14,6 +17,7 @@ from erpnext.stock.doctype.item.test_item import create_item, make_item from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError, get_serial_nos from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse +from erpnext.stock.get_item_details import update_stock from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction @@ -1199,6 +1203,8 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(pr1.items[0].rate, 100) pr1.submit() + self.assertEqual(pr1.is_internal_supplier, 1) + # Backdated purchase receipt entry, the valuation rate should be updated for DN1 and PR1 make_purchase_receipt( item_code=item_doc.name, @@ -1241,6 +1247,234 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(query[0].value, 0) + def test_backdated_transaction_for_internal_transfer_in_trasit_warehouse_for_purchase_receipt( + self, + ): + from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + + prepare_data_for_internal_transfer() + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + + from_warehouse = create_warehouse("_Test Internal From Warehouse New", company=company) + to_warehouse = create_warehouse("_Test Internal To Warehouse New", company=company) + item_doc = create_item("Test Internal Transfer Item") + + target_warehouse = create_warehouse("_Test Internal GIT Warehouse New", company=company) + + make_purchase_receipt( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -1), + warehouse=from_warehouse, + qty=1, + rate=100, + ) + + # Keep stock in advance and make sure that systen won't pick this stock while reposting backdated transaction + for i in range(1, 4): + make_purchase_receipt( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -1 * i), + warehouse=target_warehouse, + qty=1, + rate=320 * i, + ) + + dn1 = create_delivery_note( + item_code=item_doc.name, + company=company, + customer=customer, + cost_center="Main - TCP1", + expense_account="Cost of Goods Sold - TCP1", + qty=1, + rate=500, + warehouse=from_warehouse, + target_warehouse=target_warehouse, + ) + + self.assertEqual(dn1.items[0].rate, 100) + + pr1 = make_inter_company_purchase_receipt(dn1.name) + pr1.items[0].warehouse = to_warehouse + self.assertEqual(pr1.items[0].rate, 100) + pr1.submit() + + stk_ledger = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Purchase Receipt", "voucher_no": pr1.name, "warehouse": target_warehouse}, + ["stock_value_difference", "outgoing_rate"], + as_dict=True, + ) + + self.assertEqual(abs(stk_ledger.stock_value_difference), 100) + self.assertEqual(stk_ledger.outgoing_rate, 100) + + # Backdated purchase receipt entry, the valuation rate should be updated for DN1 and PR1 + make_purchase_receipt( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -2), + warehouse=from_warehouse, + qty=1, + rate=200, + ) + + dn_value = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Delivery Note", "voucher_no": dn1.name, "warehouse": target_warehouse}, + "stock_value_difference", + ) + + self.assertEqual(abs(dn_value), 200.00) + + pr_value = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Purchase Receipt", "voucher_no": pr1.name, "warehouse": to_warehouse}, + "stock_value_difference", + ) + + self.assertEqual(abs(pr_value), 200.00) + pr1.load_from_db() + + self.assertEqual(pr1.items[0].valuation_rate, 200) + self.assertEqual(pr1.items[0].rate, 100) + + Gl = frappe.qb.DocType("GL Entry") + + query = ( + frappe.qb.from_(Gl) + .select( + (fn.Sum(Gl.debit) - fn.Sum(Gl.credit)).as_("value"), + ) + .where((Gl.voucher_type == pr1.doctype) & (Gl.voucher_no == pr1.name)) + ).run(as_dict=True) + + self.assertEqual(query[0].value, 0) + + def test_backdated_transaction_for_internal_transfer_in_trasit_warehouse_for_purchase_invoice( + self, + ): + from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import ( + make_purchase_invoice as make_purchase_invoice_for_si, + ) + from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( + make_inter_company_purchase_invoice, + ) + from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice + + prepare_data_for_internal_transfer() + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + + from_warehouse = create_warehouse("_Test Internal From Warehouse New", company=company) + to_warehouse = create_warehouse("_Test Internal To Warehouse New", company=company) + item_doc = create_item("Test Internal Transfer Item") + + target_warehouse = create_warehouse("_Test Internal GIT Warehouse New", company=company) + + make_purchase_invoice_for_si( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -1), + warehouse=from_warehouse, + qty=1, + update_stock=1, + expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", + rate=100, + ) + + # Keep stock in advance and make sure that systen won't pick this stock while reposting backdated transaction + for i in range(1, 4): + make_purchase_invoice_for_si( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -1 * i), + warehouse=target_warehouse, + update_stock=1, + qty=1, + expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", + rate=320 * i, + ) + + si1 = create_sales_invoice( + item_code=item_doc.name, + company=company, + customer=customer, + cost_center="Main - TCP1", + income_account="Sales - TCP1", + qty=1, + rate=500, + update_stock=1, + warehouse=from_warehouse, + target_warehouse=target_warehouse, + ) + + self.assertEqual(si1.items[0].rate, 100) + + pi1 = make_inter_company_purchase_invoice(si1.name) + pi1.items[0].warehouse = to_warehouse + self.assertEqual(pi1.items[0].rate, 100) + pi1.update_stock = 1 + pi1.save() + pi1.submit() + + stk_ledger = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": pi1.doctype, "voucher_no": pi1.name, "warehouse": target_warehouse}, + ["stock_value_difference", "outgoing_rate"], + as_dict=True, + ) + + self.assertEqual(abs(stk_ledger.stock_value_difference), 100) + self.assertEqual(stk_ledger.outgoing_rate, 100) + + # Backdated purchase receipt entry, the valuation rate should be updated for si1 and pi1 + make_purchase_receipt( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -2), + warehouse=from_warehouse, + qty=1, + rate=200, + ) + + si_value = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": si1.doctype, "voucher_no": si1.name, "warehouse": target_warehouse}, + "stock_value_difference", + ) + + self.assertEqual(abs(si_value), 200.00) + + pi_value = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": pi1.doctype, "voucher_no": pi1.name, "warehouse": to_warehouse}, + "stock_value_difference", + ) + + self.assertEqual(abs(pi_value), 200.00) + pi1.load_from_db() + + self.assertEqual(pi1.items[0].valuation_rate, 200) + self.assertEqual(pi1.items[0].rate, 100) + + Gl = frappe.qb.DocType("GL Entry") + + query = ( + frappe.qb.from_(Gl) + .select( + (fn.Sum(Gl.debit) - fn.Sum(Gl.credit)).as_("value"), + ) + .where((Gl.voucher_type == pi1.doctype) & (Gl.voucher_no == pi1.name)) + ).run(as_dict=True) + + self.assertEqual(query[0].value, 0) + def test_batch_expiry_for_purchase_receipt(self): from erpnext.controllers.sales_and_purchase_return import make_return_doc diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 9ca40c3675..7ced1da008 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -589,6 +589,15 @@ class update_entries_after(object): sle.stock_queue = json.dumps(self.wh_data.stock_queue) sle.stock_value_difference = stock_value_difference sle.doctype = "Stock Ledger Entry" + + if ( + sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] + and sle.voucher_detail_no + and sle.actual_qty < 0 + and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier") + ): + sle.outgoing_rate = get_incoming_rate_for_inter_company_transfer(sle) + frappe.get_doc(sle).db_update() if not self.args.get("sle_id"): @@ -652,22 +661,7 @@ class update_entries_after(object): and sle.voucher_detail_no and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier") ): - field = ( - "delivery_note_item" if sle.voucher_type == "Purchase Receipt" else "sales_invoice_item" - ) - doctype = ( - "Delivery Note Item" if sle.voucher_type == "Purchase Receipt" else "Sales Invoice Item" - ) - refernce_name = frappe.get_cached_value( - sle.voucher_type + " Item", sle.voucher_detail_no, field - ) - - if refernce_name: - rate = frappe.get_cached_value( - doctype, - refernce_name, - "incoming_rate", - ) + rate = get_incoming_rate_for_inter_company_transfer(sle) else: if sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"): rate_field = "valuation_rate" @@ -748,14 +742,12 @@ class update_entries_after(object): def update_rate_on_purchase_receipt(self, sle, outgoing_rate): if frappe.db.exists(sle.voucher_type + " Item", sle.voucher_detail_no): - frappe.db.set_value( - sle.voucher_type + " Item", - sle.voucher_detail_no, - { - "base_net_rate": outgoing_rate, - "valuation_rate": outgoing_rate, - }, - ) + if sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] and frappe.get_cached_value( + sle.voucher_type, sle.voucher_no, "is_internal_supplier" + ): + frappe.db.set_value( + f"{sle.voucher_type} Item", sle.voucher_detail_no, "valuation_rate", sle.outgoing_rate + ) else: frappe.db.set_value( "Purchase Receipt Item Supplied", sle.voucher_detail_no, "rate", outgoing_rate @@ -1546,3 +1538,25 @@ def is_negative_stock_allowed(*, item_code: Optional[str] = None) -> bool: if item_code and cint(frappe.db.get_value("Item", item_code, "allow_negative_stock", cache=True)): return True return False + + +def get_incoming_rate_for_inter_company_transfer(sle) -> float: + """ + For inter company transfer, incoming rate is the average of the outgoing rate + """ + rate = 0.0 + + field = "delivery_note_item" if sle.voucher_type == "Purchase Receipt" else "sales_invoice_item" + + doctype = "Delivery Note Item" if sle.voucher_type == "Purchase Receipt" else "Sales Invoice Item" + + reference_name = frappe.get_cached_value(sle.voucher_type + " Item", sle.voucher_detail_no, field) + + if reference_name: + rate = frappe.get_cached_value( + doctype, + reference_name, + "incoming_rate", + ) + + return rate From 3266e54e33b0ac65cc8e1b51cf4d65a75c12040f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 12 Oct 2022 15:09:50 +0530 Subject: [PATCH 0346/1047] fix: consider outgoingrate while valuation rate calculate --- erpnext/stock/stock_ledger.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 7ced1da008..cdf6e89fcb 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -542,6 +542,14 @@ class update_entries_after(object): if not self.args.get("sle_id"): self.get_dynamic_incoming_outgoing_rate(sle) + if ( + sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] + and sle.voucher_detail_no + and sle.actual_qty < 0 + and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier") + ): + sle.outgoing_rate = get_incoming_rate_for_inter_company_transfer(sle) + if get_serial_nos(sle.serial_no): self.get_serialized_values(sle) self.wh_data.qty_after_transaction += flt(sle.actual_qty) @@ -590,14 +598,6 @@ class update_entries_after(object): sle.stock_value_difference = stock_value_difference sle.doctype = "Stock Ledger Entry" - if ( - sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] - and sle.voucher_detail_no - and sle.actual_qty < 0 - and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier") - ): - sle.outgoing_rate = get_incoming_rate_for_inter_company_transfer(sle) - frappe.get_doc(sle).db_update() if not self.args.get("sle_id"): From 98bf8e13043a5efcf4b92a02d9c0c1e6ae67b3e8 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 12 Oct 2022 15:45:36 +0530 Subject: [PATCH 0347/1047] fix: test case --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index b548ea3c00..52e221ca9d 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3306,7 +3306,7 @@ def create_sales_invoice(**args): "item_name": args.item_name or "_Test Item", "description": args.description or "_Test Item", "warehouse": args.warehouse or "_Test Warehouse - _TC", - "target_warehouse": args.target_warehouse or "_Test Warehouse 1 - _TC", + "target_warehouse": args.target_warehouse, "qty": args.qty or 1, "uom": args.uom or "Nos", "stock_uom": args.uom or "Nos", From 9aa5e20ef7a466713e33a35f35d3219b36358b51 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 12 Oct 2022 15:53:28 +0530 Subject: [PATCH 0348/1047] chore: check only for inter-company transfers --- erpnext/controllers/stock_controller.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 31c480f98d..98dc58677b 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -193,9 +193,7 @@ class StockController(AccountsController): elif sle.warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(sle.warehouse) - if abs(sle_rounding_diff) > (1.0 / (10**precision)) and ( - self.get("is_internal_customer") or self.get("is_internal_supplier") - ): + if abs(sle_rounding_diff) > (1.0 / (10**precision)) and self.is_internal_transfer(): warehouse_asset_account = "" if self.get("is_internal_customer"): warehouse_asset_account = warehouse_account[item_row.get("target_warehouse")]["account"] From eb819368aa9a8700ee868cbb8375aa893e9de72f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 11 Oct 2022 15:27:28 +0530 Subject: [PATCH 0349/1047] test: dedeplication on payment ledger upon repost --- .../test_repost_item_valuation.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py index edd2553d5d..e0f2479710 100644 --- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py @@ -9,6 +9,7 @@ from frappe.tests.utils import FrappeTestCase from frappe.utils import nowdate from frappe.utils.data import add_to_date, today +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.utils import repost_gle_for_stock_vouchers from erpnext.controllers.stock_controller import create_item_wise_repost_entries from erpnext.stock.doctype.item.test_item import make_item @@ -272,3 +273,57 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin): [{"credit": 50, "debit": 0}], gle_filters={"account": "Stock In Hand - TCP1"}, ) + + def test_duplicate_ple_on_repost(self): + from erpnext.accounts import utils + + # lower numbers to simplify test + orig_chunk_size = utils.GL_REPOSTING_CHUNK + utils.GL_REPOSTING_CHUNK = 2 + self.addCleanup(setattr, utils, "GL_REPOSTING_CHUNK", orig_chunk_size) + + rate = 100 + item = self.make_item() + item.valuation_rate = 90 + item.allow_negative_stock = 1 + item.save() + + company = "_Test Company with perpetual inventory" + + # consume non-existing stock + sinv = create_sales_invoice( + company=company, + posting_date=today(), + debit_to="Debtors - TCP1", + income_account="Sales - TCP1", + expense_account="Cost of Goods Sold - TCP1", + warehouse="Stores - TCP1", + update_stock=1, + currency="INR", + item_code=item.name, + cost_center="Main - TCP1", + qty=1, + rate=rate, + ) + + # backdated receipt triggers repost + make_stock_entry( + item=item.name, + company=company, + qty=5, + rate=rate, + target="Stores - TCP1", + posting_date=add_to_date(today(), days=-1), + ) + + ple_entries = frappe.db.get_list( + "Payment Ledger Entry", + filters={"voucher_type": sinv.doctype, "voucher_no": sinv.name, "delinked": 0}, + ) + + # assert successful deduplication on PLE + self.assertEqual(len(ple_entries), 1) + + # outstanding should not be affected + sinv.reload() + self.assertEqual(sinv.outstanding_amount, 100) From 692462d7c88a6f33683bd9709670f374112bfa99 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 12 Oct 2022 16:30:48 +0530 Subject: [PATCH 0350/1047] fix: removed unnecessary imports --- .../stock/doctype/purchase_receipt/test_purchase_receipt.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 1f807b8249..dc9f2b2117 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1,10 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt - -from cmath import pi -from turtle import update - import frappe from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, cint, cstr, flt, today @@ -17,7 +13,6 @@ from erpnext.stock.doctype.item.test_item import create_item, make_item from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError, get_serial_nos from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse -from erpnext.stock.get_item_details import update_stock from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction From e543dca6a0220b3e77560c6c2beddc32c6672f00 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Thu, 13 Oct 2022 08:23:52 +0200 Subject: [PATCH 0351/1047] fix: don't try to update youtube data if disabled in settings (#32588) fix: cast value from db [skip ci] --- erpnext/utilities/doctype/video/video.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/utilities/doctype/video/video.py b/erpnext/utilities/doctype/video/video.py index 15dbccde89..13b7877b21 100644 --- a/erpnext/utilities/doctype/video/video.py +++ b/erpnext/utilities/doctype/video/video.py @@ -59,7 +59,7 @@ def update_youtube_data(): "Video Settings", "Video Settings", ["enable_youtube_tracking", "frequency"] ) - if not enable_youtube_tracking: + if not cint(enable_youtube_tracking): return frequency = get_frequency(frequency) From edc93ab6aae421bdec9bcb5dc1a385af169fc279 Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Thu, 13 Oct 2022 12:00:30 +0530 Subject: [PATCH 0352/1047] fix: pricing rule item group uom Handle use case where pricing rule is applied on item group and user have selected UOM. pricing rule was applied on all UOM's even after specifying UOM. i have added condition in sql to fix this. --- erpnext/accounts/doctype/pricing_rule/utils.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 1f29d732ba..11ae540efa 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -121,6 +121,12 @@ def _get_pricing_rules(apply_on, args, values): values["variant_of"] = args.variant_of elif apply_on_field == "item_group": item_conditions = _get_tree_conditions(args, "Item Group", child_doc, False) + if args.get("uom", None): + item_conditions += ( + " and ({child_doc}.uom='{item_uom}' or IFNULL({child_doc}.uom, '')='')".format( + child_doc=child_doc, item_uom=args.get("uom") + ) + ) conditions += get_other_conditions(conditions, values, args) warehouse_conditions = _get_tree_conditions(args, "Warehouse", "`tabPricing Rule`") From 56a4a7739882259ecce97896dbf626adc6177502 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 13 Oct 2022 12:31:30 +0530 Subject: [PATCH 0353/1047] fix: Renamed Notes section to Comments --- erpnext/crm/doctype/lead/lead.json | 4 ++-- erpnext/crm/doctype/opportunity/opportunity.json | 4 ++-- erpnext/crm/doctype/prospect/prospect.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index 99c00ad6e6..6c8cb2f080 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -375,7 +375,7 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "notes_tab", "fieldtype": "Tab Break", - "label": "Notes" + "label": "Comments" }, { "collapsible": 1, @@ -514,7 +514,7 @@ "idx": 5, "image_field": "image", "links": [], - "modified": "2022-08-09 18:26:17.101521", + "modified": "2022-10-13 12:28:47.241586", "modified_by": "Administrator", "module": "CRM", "name": "Lead", diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index fed0c7c79a..36c98dc279 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -551,7 +551,7 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "notes_tab", "fieldtype": "Tab Break", - "label": "Notes" + "label": "Comments" }, { "fieldname": "notes_html", @@ -622,7 +622,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2022-08-09 18:26:37.235964", + "modified": "2022-10-13 12:29:18.233542", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", diff --git a/erpnext/crm/doctype/prospect/prospect.json b/erpnext/crm/doctype/prospect/prospect.json index 820a6c72ea..d32311bc4e 100644 --- a/erpnext/crm/doctype/prospect/prospect.json +++ b/erpnext/crm/doctype/prospect/prospect.json @@ -128,7 +128,7 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "notes_section", "fieldtype": "Tab Break", - "label": "Notes" + "label": "Comments" }, { "depends_on": "eval: !doc.__islocal", @@ -218,7 +218,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2022-08-09 18:26:56.950185", + "modified": "2022-10-13 12:29:33.674561", "modified_by": "Administrator", "module": "CRM", "name": "Prospect", From f561d8f6895a07573b9fef134945da6c9739e04f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 13 Oct 2022 12:43:08 +0530 Subject: [PATCH 0354/1047] fix: Renamed Dashboard tab label to Connections --- erpnext/crm/doctype/lead/lead.json | 4 ++-- erpnext/crm/doctype/opportunity/opportunity.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index 6c8cb2f080..8f8a086d99 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -506,7 +506,7 @@ { "fieldname": "dashboard_tab", "fieldtype": "Tab Break", - "label": "Dashboard", + "label": "Connections", "show_dashboard": 1 } ], @@ -514,7 +514,7 @@ "idx": 5, "image_field": "image", "links": [], - "modified": "2022-10-13 12:28:47.241586", + "modified": "2022-10-13 12:42:04.277879", "modified_by": "Administrator", "module": "CRM", "name": "Lead", diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 36c98dc279..07641d20c3 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -544,7 +544,7 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "dashboard_tab", "fieldtype": "Tab Break", - "label": "Dashboard", + "label": "Connections", "show_dashboard": 1 }, { @@ -622,7 +622,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2022-10-13 12:29:18.233542", + "modified": "2022-10-13 12:42:21.545636", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", From fd49503ba2dc19f07a3a8dce36812be753587e08 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 13 Oct 2022 15:08:34 +0530 Subject: [PATCH 0355/1047] fix: Party account for multi-order invoices --- erpnext/buying/doctype/purchase_order/purchase_order.py | 3 ++- erpnext/selling/doctype/sales_order/sales_order.py | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index bcedd4d0a1..c224b611e5 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -18,7 +18,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import ( get_party_tax_withholding_details, ) -from erpnext.accounts.party import get_party_account_currency +from erpnext.accounts.party import get_party_account, get_party_account_currency from erpnext.buying.utils import check_on_hold_or_closed_status, validate_for_items from erpnext.controllers.buying_controller import BuyingController from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults @@ -558,6 +558,7 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions target.set_advances() target.set_payment_schedule() + target.credit_to = get_party_account("Supplier", source.supplier, source.company) def update_item(obj, target, source_parent): target.amount = flt(obj.amount) - flt(obj.billed_amt) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 25806d6ed8..f0e9e4b7d9 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -18,6 +18,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( update_linked_doc, validate_inter_company_party, ) +from erpnext.accounts.party import get_party_account from erpnext.controllers.selling_controller import SellingController from erpnext.manufacturing.doctype.production_plan.production_plan import ( get_items_for_material_requests, @@ -727,6 +728,8 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): if source.loyalty_points and source.order_type == "Shopping Cart": target.redeem_loyalty_points = 1 + target.debit_to = get_party_account("Customer", source.customer, source.company) + def update_item(source, target, source_parent): target.amount = flt(source.amount) - flt(source.billed_amt) target.base_amount = target.amount * flt(source_parent.conversion_rate) From 50e96989324f475fc386882eb8a8dc1df4a8c975 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 13 Oct 2022 15:25:22 +0530 Subject: [PATCH 0356/1047] chore: drop dead code (#32595) [skip ci] --- erpnext/hooks.py | 2 - erpnext/startup/report_data_map.py | 327 ----------------------------- 2 files changed, 329 deletions(-) delete mode 100644 erpnext/startup/report_data_map.py diff --git a/erpnext/hooks.py b/erpnext/hooks.py index b8f51f839c..6bc17a3675 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -274,8 +274,6 @@ has_website_permission = { "Timesheet": "erpnext.controllers.website_list_for_contact.has_website_permission", } -dump_report_map = "erpnext.startup.report_data_map.data_map" - before_tests = "erpnext.setup.utils.before_tests" standard_queries = { diff --git a/erpnext/startup/report_data_map.py b/erpnext/startup/report_data_map.py deleted file mode 100644 index f8c1b6cca0..0000000000 --- a/erpnext/startup/report_data_map.py +++ /dev/null @@ -1,327 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -# mappings for table dumps -# "remember to add indexes!" - -data_map = { - "Company": {"columns": ["name"], "conditions": ["docstatus < 2"]}, - "Fiscal Year": { - "columns": ["name", "year_start_date", "year_end_date"], - "conditions": ["docstatus < 2"], - }, - # Accounts - "Account": { - "columns": ["name", "parent_account", "lft", "rgt", "report_type", "company", "is_group"], - "conditions": ["docstatus < 2"], - "order_by": "lft", - "links": { - "company": ["Company", "name"], - }, - }, - "Cost Center": { - "columns": ["name", "lft", "rgt"], - "conditions": ["docstatus < 2"], - "order_by": "lft", - }, - "GL Entry": { - "columns": [ - "name", - "account", - "posting_date", - "cost_center", - "debit", - "credit", - "is_opening", - "company", - "voucher_type", - "voucher_no", - "remarks", - ], - "order_by": "posting_date, account", - "links": { - "account": ["Account", "name"], - "company": ["Company", "name"], - "cost_center": ["Cost Center", "name"], - }, - }, - # Stock - "Item": { - "columns": [ - "name", - "if(item_name=name, '', item_name) as item_name", - "description", - "item_group as parent_item_group", - "stock_uom", - "brand", - "valuation_method", - ], - # "conditions": ["docstatus < 2"], - "order_by": "name", - "links": {"parent_item_group": ["Item Group", "name"], "brand": ["Brand", "name"]}, - }, - "Item Group": { - "columns": ["name", "parent_item_group"], - # "conditions": ["docstatus < 2"], - "order_by": "lft", - }, - "Brand": {"columns": ["name"], "conditions": ["docstatus < 2"], "order_by": "name"}, - "Project": {"columns": ["name"], "conditions": ["docstatus < 2"], "order_by": "name"}, - "Warehouse": {"columns": ["name"], "conditions": ["docstatus < 2"], "order_by": "name"}, - "Stock Ledger Entry": { - "columns": [ - "name", - "posting_date", - "posting_time", - "item_code", - "warehouse", - "actual_qty as qty", - "voucher_type", - "voucher_no", - "project", - "incoming_rate as incoming_rate", - "stock_uom", - "serial_no", - "qty_after_transaction", - "valuation_rate", - ], - "order_by": "posting_date, posting_time, creation", - "links": { - "item_code": ["Item", "name"], - "warehouse": ["Warehouse", "name"], - "project": ["Project", "name"], - }, - "force_index": "posting_sort_index", - }, - "Serial No": { - "columns": ["name", "purchase_rate as incoming_rate"], - "conditions": ["docstatus < 2"], - "order_by": "name", - }, - "Stock Entry": { - "columns": ["name", "purpose"], - "conditions": ["docstatus=1"], - "order_by": "posting_date, posting_time, name", - }, - "Material Request Item": { - "columns": ["item.name as name", "item_code", "warehouse", "(qty - ordered_qty) as qty"], - "from": "`tabMaterial Request Item` item, `tabMaterial Request` main", - "conditions": [ - "item.parent = main.name", - "main.docstatus=1", - "main.status != 'Stopped'", - "ifnull(warehouse, '')!=''", - "qty > ordered_qty", - ], - "links": {"item_code": ["Item", "name"], "warehouse": ["Warehouse", "name"]}, - }, - "Purchase Order Item": { - "columns": [ - "item.name as name", - "item_code", - "warehouse", - "(qty - received_qty)*conversion_factor as qty", - ], - "from": "`tabPurchase Order Item` item, `tabPurchase Order` main", - "conditions": [ - "item.parent = main.name", - "main.docstatus=1", - "main.status != 'Stopped'", - "ifnull(warehouse, '')!=''", - "qty > received_qty", - ], - "links": {"item_code": ["Item", "name"], "warehouse": ["Warehouse", "name"]}, - }, - "Sales Order Item": { - "columns": [ - "item.name as name", - "item_code", - "(qty - delivered_qty)*conversion_factor as qty", - "warehouse", - ], - "from": "`tabSales Order Item` item, `tabSales Order` main", - "conditions": [ - "item.parent = main.name", - "main.docstatus=1", - "main.status != 'Stopped'", - "ifnull(warehouse, '')!=''", - "qty > delivered_qty", - ], - "links": {"item_code": ["Item", "name"], "warehouse": ["Warehouse", "name"]}, - }, - # Sales - "Customer": { - "columns": [ - "name", - "if(customer_name=name, '', customer_name) as customer_name", - "customer_group as parent_customer_group", - "territory as parent_territory", - ], - "conditions": ["docstatus < 2"], - "order_by": "name", - "links": { - "parent_customer_group": ["Customer Group", "name"], - "parent_territory": ["Territory", "name"], - }, - }, - "Customer Group": { - "columns": ["name", "parent_customer_group"], - "conditions": ["docstatus < 2"], - "order_by": "lft", - }, - "Territory": { - "columns": ["name", "parent_territory"], - "conditions": ["docstatus < 2"], - "order_by": "lft", - }, - "Sales Invoice": { - "columns": ["name", "customer", "posting_date", "company"], - "conditions": ["docstatus=1"], - "order_by": "posting_date", - "links": {"customer": ["Customer", "name"], "company": ["Company", "name"]}, - }, - "Sales Invoice Item": { - "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"], - "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], - "order_by": "parent", - "links": {"parent": ["Sales Invoice", "name"], "item_code": ["Item", "name"]}, - }, - "Sales Order": { - "columns": ["name", "customer", "transaction_date as posting_date", "company"], - "conditions": ["docstatus=1"], - "order_by": "transaction_date", - "links": {"customer": ["Customer", "name"], "company": ["Company", "name"]}, - }, - "Sales Order Item[Sales Analytics]": { - "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"], - "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], - "order_by": "parent", - "links": {"parent": ["Sales Order", "name"], "item_code": ["Item", "name"]}, - }, - "Delivery Note": { - "columns": ["name", "customer", "posting_date", "company"], - "conditions": ["docstatus=1"], - "order_by": "posting_date", - "links": {"customer": ["Customer", "name"], "company": ["Company", "name"]}, - }, - "Delivery Note Item[Sales Analytics]": { - "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"], - "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], - "order_by": "parent", - "links": {"parent": ["Delivery Note", "name"], "item_code": ["Item", "name"]}, - }, - "Supplier": { - "columns": [ - "name", - "if(supplier_name=name, '', supplier_name) as supplier_name", - "supplier_group as parent_supplier_group", - ], - "conditions": ["docstatus < 2"], - "order_by": "name", - "links": { - "parent_supplier_group": ["Supplier Group", "name"], - }, - }, - "Supplier Group": { - "columns": ["name", "parent_supplier_group"], - "conditions": ["docstatus < 2"], - "order_by": "name", - }, - "Purchase Invoice": { - "columns": ["name", "supplier", "posting_date", "company"], - "conditions": ["docstatus=1"], - "order_by": "posting_date", - "links": {"supplier": ["Supplier", "name"], "company": ["Company", "name"]}, - }, - "Purchase Invoice Item": { - "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"], - "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], - "order_by": "parent", - "links": {"parent": ["Purchase Invoice", "name"], "item_code": ["Item", "name"]}, - }, - "Purchase Order": { - "columns": ["name", "supplier", "transaction_date as posting_date", "company"], - "conditions": ["docstatus=1"], - "order_by": "posting_date", - "links": {"supplier": ["Supplier", "name"], "company": ["Company", "name"]}, - }, - "Purchase Order Item[Purchase Analytics]": { - "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"], - "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], - "order_by": "parent", - "links": {"parent": ["Purchase Order", "name"], "item_code": ["Item", "name"]}, - }, - "Purchase Receipt": { - "columns": ["name", "supplier", "posting_date", "company"], - "conditions": ["docstatus=1"], - "order_by": "posting_date", - "links": {"supplier": ["Supplier", "name"], "company": ["Company", "name"]}, - }, - "Purchase Receipt Item[Purchase Analytics]": { - "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"], - "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], - "order_by": "parent", - "links": {"parent": ["Purchase Receipt", "name"], "item_code": ["Item", "name"]}, - }, - # Support - "Issue": { - "columns": ["name", "status", "creation", "resolution_date", "first_responded_on"], - "conditions": ["docstatus < 2"], - "order_by": "creation", - }, - # Manufacturing - "Work Order": { - "columns": [ - "name", - "status", - "creation", - "planned_start_date", - "planned_end_date", - "status", - "actual_start_date", - "actual_end_date", - "modified", - ], - "conditions": ["docstatus = 1"], - "order_by": "creation", - }, - # Medical - "Patient": { - "columns": [ - "name", - "creation", - "owner", - "if(patient_name=name, '', patient_name) as patient_name", - ], - "conditions": ["docstatus < 2"], - "order_by": "name", - "links": {"owner": ["User", "name"]}, - }, - "Patient Appointment": { - "columns": [ - "name", - "appointment_type", - "patient", - "practitioner", - "appointment_date", - "department", - "status", - "company", - ], - "order_by": "name", - "links": { - "practitioner": ["Healthcare Practitioner", "name"], - "appointment_type": ["Appointment Type", "name"], - }, - }, - "Healthcare Practitioner": { - "columns": ["name", "department"], - "order_by": "name", - "links": { - "department": ["Department", "name"], - }, - }, - "Appointment Type": {"columns": ["name"], "order_by": "name"}, - "Medical Department": {"columns": ["name"], "order_by": "name"}, -} From 9127b960333a6e81ec6c81978b3e8ada7fc2b327 Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Thu, 13 Oct 2022 18:27:11 +0530 Subject: [PATCH 0357/1047] fix: add project settings to projects workspace (#32568) --- .../projects/workspace/projects/projects.json | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/workspace/projects/projects.json b/erpnext/projects/workspace/projects/projects.json index 1253649e49..4bdb1db387 100644 --- a/erpnext/projects/workspace/projects/projects.json +++ b/erpnext/projects/workspace/projects/projects.json @@ -5,7 +5,7 @@ "label": "Open Projects" } ], - "content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Open Projects\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Task\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Timesheet\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project Billing Summary\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Projects\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Time Tracking\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]", + "content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Open Projects\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Task\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Timesheet\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project Billing Summary\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Projects\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Time Tracking\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", "creation": "2020-03-02 15:46:04.874669", "docstatus": 0, "doctype": "Workspace", @@ -170,9 +170,27 @@ "link_type": "Report", "onboard": 0, "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "link_count": 1, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Projects Settings", + "link_count": 0, + "link_to": "Projects Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" } ], - "modified": "2022-06-28 12:31:30.167740", + "modified": "2022-10-11 22:39:10.436311", "modified_by": "Administrator", "module": "Projects", "name": "Projects", From 30da6ab2c1b870047d78b0f52196c906c6d3533d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 17 Oct 2022 11:10:38 +0530 Subject: [PATCH 0358/1047] feat: Editable Sales Invoice --- .../doctype/sales_invoice/sales_invoice.js | 21 ++++++ .../doctype/sales_invoice/sales_invoice.json | 12 +++- .../doctype/sales_invoice/sales_invoice.py | 70 ++++++++++++++++++- 3 files changed, 99 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 73ec051c6d..7a5d3922f3 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -64,6 +64,27 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e this.frm.toggle_reqd("due_date", !this.frm.doc.is_return); + if (this.frm.doc.repost_required) { + this.frm.set_intro(__("Accounting entries for this invoice needs to be reposted. Please click on 'Repost' button to update.")); + this.frm.add_custom_button(__('Repost Accounting Entries'), + () => { + this.frm.call({ + doc: this.frm.doc, + method: 'repost_accounting_entries', + freeze: true, + freeze_message: __('Reposting...'), + callback: (r) => { + if (!r.exc) { + frappe.msgprint(__('Accounting Entries are reposted')); + this.frm.trigger('refresh'); + } + } + }); + }); + + $(`["${encodeURIComponent("Repost Accounting Entries")}"]`).css('color', 'red'); + } + if (this.frm.doc.is_return) { this.frm.return_print_format = "Sales Invoice Return"; } diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 97e5f4017e..b98cd3ad67 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -207,6 +207,7 @@ "is_internal_customer", "is_discounted", "remarks", + "repost_required", "connections_tab" ], "fields": [ @@ -1703,6 +1704,7 @@ "read_only": 1 }, { + "allow_on_submit": 1, "default": "No", "fieldname": "is_opening", "fieldtype": "Select", @@ -2097,6 +2099,14 @@ "hide_seconds": 1, "label": "Write Off", "width": "50%" + }, + { + "default": "0", + "fieldname": "repost_required", + "fieldtype": "Check", + "hidden": 1, + "label": "Repost Required", + "read_only": 1 } ], "icon": "fa fa-file-text", @@ -2109,7 +2119,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2022-10-11 13:07:36.488095", + "modified": "2022-10-15 19:15:49.526529", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index afd5a59df4..4c38883913 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -11,6 +11,9 @@ from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form import erpnext from erpnext.accounts.deferred_revenue import validate_service_stop_date +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + get_accounting_dimensions, +) from erpnext.accounts.doctype.loyalty_program.loyalty_program import ( get_loyalty_program_details_with_points, validate_loyalty_points, @@ -100,13 +103,11 @@ class SalesInvoice(SellingController): self.validate_debit_to_acc() self.clear_unallocated_advances("Sales Invoice Advance", "advances") self.add_remarks() - self.validate_write_off_account() - self.validate_account_for_change_amount() self.validate_fixed_asset() self.set_income_account_for_fixed_assets() self.validate_item_cost_centers() - self.validate_income_account() self.check_conversion_rate() + self.validate_accounts() validate_inter_company_party( self.doctype, self.customer, self.company, self.inter_company_invoice_reference @@ -170,6 +171,11 @@ class SalesInvoice(SellingController): self.reset_default_field_value("set_warehouse", "items", "warehouse") + def validate_accounts(self): + self.validate_write_off_account() + self.validate_account_for_change_amount() + self.validate_income_account() + def validate_fixed_asset(self): for d in self.get("items"): if d.is_fixed_asset and d.meta.get_field("asset") and d.asset: @@ -514,6 +520,64 @@ class SalesInvoice(SellingController): def on_update(self): self.set_paid_amount() + def on_update_after_submit(self): + needs_repost = 0 + # Check if any field affecting accounting entry is altered + doc_before_update = self.get_doc_before_save() + accounting_dimensions = get_accounting_dimensions() + + # Check if opening entry check updated + if doc_before_update.get("is_opening") != self.is_opening: + needs_repost = 1 + + if not needs_repost: + # Parent Level Accounts excluding party account + for field in ( + "additional_discount_account", + "cash_bank_account", + "account_for_change_amount", + "write_off_account", + "loyalty_redemption_account", + "unrealized_profit_loss_account", + ): + if doc_before_update.get(field) != self.get(field): + needs_repost = 1 + break + + # Check for parent accounting dimensions + for dimension in accounting_dimensions: + if doc_before_update.get(dimension) != self.get(dimension): + needs_repost = 1 + break + + # Check for parent level + for index, item in enumerate(self.get("items")): + for field in ( + "income_account", + "expense_account", + "discount_account", + "deferred_revenue_account", + ): + if doc_before_update.get("items")[index].get(field) != item.get(field): + needs_repost = 1 + break + + for dimension in accounting_dimensions: + if doc_before_update.get("items")[index].get(dimension) != item.get(dimension): + needs_repost = 1 + break + + self.validate_accounts() + self.db_set("repost_required", needs_repost) + + @frappe.whitelist() + def repost_accounting_entries(self): + self.docstatus = 2 + self.make_gl_entries_on_cancel() + self.docstatus = 1 + self.make_gl_entries() + self.db_set("repost_required", 0) + def set_paid_amount(self): paid_amount = 0.0 base_paid_amount = 0.0 From dc3fe8592187bf543eae628c639868de84ad9006 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 17 Oct 2022 11:37:50 +0530 Subject: [PATCH 0359/1047] chore: Remove HRMS related code (#32607) Co-authored-by: Rucha Mahabal --- .../journal_entry_account/journal_entry_account.json | 12 +++++++++--- erpnext/accounts/utils.py | 4 ---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index dff883aef9..a0ea43332c 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -202,6 +202,7 @@ "fieldname": "reference_type", "fieldtype": "Select", "label": "Reference Type", + "no_copy": 1, "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement" }, { @@ -209,13 +210,15 @@ "fieldtype": "Dynamic Link", "in_list_view": 1, "label": "Reference Name", + "no_copy": 1, "options": "reference_type" }, { "depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])", "fieldname": "reference_due_date", "fieldtype": "Select", - "label": "Reference Due Date" + "label": "Reference Due Date", + "no_copy": 1 }, { "fieldname": "project", @@ -274,19 +277,22 @@ "fieldname": "reference_detail_no", "fieldtype": "Data", "hidden": 1, - "label": "Reference Detail No" + "label": "Reference Detail No", + "no_copy": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-08-30 21:27:32.200299", + "modified": "2022-10-13 17:07:17.999191", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", + "naming_rule": "Random", "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 02dcd683fa..67574ca992 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -460,10 +460,6 @@ def reconcile_against_document(args): # nosemgrep frappe.flags.ignore_party_validation = False - if entry.voucher_type in ("Payment Entry", "Journal Entry"): - if hasattr(doc, "update_expense_claim"): - doc.update_expense_claim() - def check_if_advance_entry_modified(args): """ From 6381e75fa5b402a678c94320eba14b19a3c11012 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 17 Oct 2022 12:17:05 +0530 Subject: [PATCH 0360/1047] fix: group warehouse filter not working for Batchwise Balance history report --- .../batch_wise_balance_history.py | 67 +++++++++++++------ 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index 8a13300dc8..911f4c3167 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -5,6 +5,8 @@ import frappe from frappe import _ from frappe.utils import cint, flt, getdate +from pypika import functions as fn +from pypika.terms import ExistsCriterion def execute(filters=None): @@ -64,36 +66,61 @@ def get_columns(filters): return columns -def get_conditions(filters): - conditions = "" +# get all details +def get_stock_ledger_entries(filters): if not filters.get("from_date"): frappe.throw(_("'From Date' is required")) - if filters.get("to_date"): - conditions += " and posting_date <= '%s'" % filters["to_date"] + sle = frappe.qb.DocType("Stock Ledger Entry") + query = ( + frappe.qb.from_(sle) + .select( + sle.item_code, + sle.warehouse, + sle.batch_no, + sle.posting_date, + fn.Sum(sle.actual_qty).as_("actual_qty"), + ) + .where( + (sle.docstatus < 2) + & (sle.is_cancelled == 0) + & (sle.batch_no.isnotnull()) + & (sle.batch_no != "") + ) + .groupby(sle.voucher_no, sle.batch_no, sle.item_code, sle.warehouse) + .orderby(sle.item_code, sle.warehouse) + ) + + if to_date := filters.get("to_date"): + query = query.where(sle.posting_date <= to_date) else: frappe.throw(_("'To Date' is required")) - for field in ["item_code", "warehouse", "batch_no", "company"]: + query = apply_warehouse_filter(query, sle, filters) + for field in ["item_code", "batch_no", "company"]: if filters.get(field): - conditions += " and {0} = {1}".format(field, frappe.db.escape(filters.get(field))) + query = query.where(sle[field] == filters.get(field)) - return conditions + return query.run(as_dict=True) -# get all details -def get_stock_ledger_entries(filters): - conditions = get_conditions(filters) - return frappe.db.sql( - """ - select item_code, batch_no, warehouse, posting_date, sum(actual_qty) as actual_qty - from `tabStock Ledger Entry` - where is_cancelled = 0 and docstatus < 2 and ifnull(batch_no, '') != '' %s - group by voucher_no, batch_no, item_code, warehouse - order by item_code, warehouse""" - % conditions, - as_dict=1, - ) +def apply_warehouse_filter(query, sle, filters): + if warehouse := filters.get("warehouse"): + warehouse_table = frappe.qb.DocType("Warehouse") + + lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) + chilren_subquery = ( + frappe.qb.from_(warehouse_table) + .select(warehouse_table.name) + .where( + (warehouse_table.lft >= lft) + & (warehouse_table.rgt <= rgt) + & (warehouse_table.name == sle.warehouse) + ) + ) + query = query.where(ExistsCriterion(chilren_subquery)) + + return query def get_item_warehouse_batch_map(filters, float_precision): From 2481574a2808e888acc0b59492642e0ffd5d9376 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 17 Oct 2022 14:18:55 +0530 Subject: [PATCH 0361/1047] chore: seperate function to apply filter for warehouse in case of QB --- erpnext/stock/doctype/warehouse/warehouse.py | 21 ++++++++++++++++++ .../batch_wise_balance_history.py | 22 ++----------------- .../report/stock_balance/stock_balance.py | 16 +++----------- .../stock/report/stock_ledger/stock_ledger.py | 17 ++------------ 4 files changed, 28 insertions(+), 48 deletions(-) diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index ab784ca107..6e06d23617 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -9,6 +9,7 @@ from frappe import _, throw from frappe.contacts.address_and_contact import load_address_and_contact from frappe.utils import cint, flt from frappe.utils.nestedset import NestedSet +from pypika.terms import ExistsCriterion from erpnext.stock import get_warehouse_account @@ -266,3 +267,23 @@ def get_warehouses_based_on_account(account, company=None): frappe.throw(_("Warehouse not found against the account {0}").format(account)) return warehouses + + +# Will be use for frappe.qb +def apply_warehouse_filter(query, sle, filters): + if warehouse := filters.get("warehouse"): + warehouse_table = frappe.qb.DocType("Warehouse") + + lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) + chilren_subquery = ( + frappe.qb.from_(warehouse_table) + .select(warehouse_table.name) + .where( + (warehouse_table.lft >= lft) + & (warehouse_table.rgt <= rgt) + & (warehouse_table.name == sle.warehouse) + ) + ) + query = query.where(ExistsCriterion(chilren_subquery)) + + return query diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index 911f4c3167..291c6b5dab 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -6,7 +6,8 @@ import frappe from frappe import _ from frappe.utils import cint, flt, getdate from pypika import functions as fn -from pypika.terms import ExistsCriterion + +from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter def execute(filters=None): @@ -104,25 +105,6 @@ def get_stock_ledger_entries(filters): return query.run(as_dict=True) -def apply_warehouse_filter(query, sle, filters): - if warehouse := filters.get("warehouse"): - warehouse_table = frappe.qb.DocType("Warehouse") - - lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) - chilren_subquery = ( - frappe.qb.from_(warehouse_table) - .select(warehouse_table.name) - .where( - (warehouse_table.lft >= lft) - & (warehouse_table.rgt <= rgt) - & (warehouse_table.name == sle.warehouse) - ) - ) - query = query.where(ExistsCriterion(chilren_subquery)) - - return query - - def get_item_warehouse_batch_map(filters, float_precision): sle = get_stock_ledger_entries(filters) iwb_map = {} diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 679d234c9f..0fc642ef20 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -10,10 +10,10 @@ from frappe import _ from frappe.query_builder.functions import CombineDatetime from frappe.utils import cint, date_diff, flt, getdate from frappe.utils.nestedset import get_descendants_of -from pypika.terms import ExistsCriterion import erpnext from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions +from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, get_average_age from erpnext.stock.utils import add_additional_uom_columns, is_reposting_item_valuation_in_progress @@ -270,18 +270,8 @@ def apply_conditions(query, filters): if company := filters.get("company"): query = query.where(sle.company == company) - if warehouse := filters.get("warehouse"): - lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) - chilren_subquery = ( - frappe.qb.from_(warehouse_table) - .select(warehouse_table.name) - .where( - (warehouse_table.lft >= lft) - & (warehouse_table.rgt <= rgt) - & (warehouse_table.name == sle.warehouse) - ) - ) - query = query.where(ExistsCriterion(chilren_subquery)) + if filters.get("warehouse"): + query = apply_warehouse_filter(query, sle, filters) elif warehouse_type := filters.get("warehouse_type"): query = ( query.join(warehouse_table) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index e18d4c7522..a95119736a 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -6,11 +6,11 @@ import frappe from frappe import _ from frappe.query_builder.functions import CombineDatetime from frappe.utils import cint, flt -from pypika.terms import ExistsCriterion from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import get_stock_balance_for +from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter from erpnext.stock.utils import ( is_reposting_item_valuation_in_progress, update_included_uom_in_report, @@ -295,20 +295,7 @@ def get_stock_ledger_entries(filters, items): if filters.get(field): query = query.where(sle[field] == filters.get(field)) - if warehouse := filters.get("warehouse"): - lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) - - warehouse_table = frappe.qb.DocType("Warehouse") - chilren_subquery = ( - frappe.qb.from_(warehouse_table) - .select(warehouse_table.name) - .where( - (warehouse_table.lft >= lft) - & (warehouse_table.rgt <= rgt) - & (warehouse_table.name == sle.warehouse) - ) - ) - query = query.where(ExistsCriterion(chilren_subquery)) + query = apply_warehouse_filter(query, sle, filters) return query.run(as_dict=True) From ced8d2a5376e5b95e6b798a2b3b8975dbd88f1c5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 17 Oct 2022 16:21:54 +0530 Subject: [PATCH 0362/1047] test: lead creation and deletion restricted to dummy company --- .../test_service_level_agreement.py | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 4e00138906..472f6bc24e 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -15,8 +15,30 @@ from erpnext.support.doctype.service_level_agreement.service_level_agreement imp class TestServiceLevelAgreement(unittest.TestCase): def setUp(self): + self.create_company() frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) - frappe.db.sql("delete from `tabLead`") + lead = frappe.qb.DocType("Lead") + frappe.qb.from_(lead).delete().where(lead.company == self.company).run() + + def create_company(self): + name = "_Test Support SLA" + company = None + if frappe.db.exists("Company", name): + company = frappe.get_doc("Company", name) + else: + company = frappe.get_doc( + { + "doctype": "Company", + "company_name": name, + "country": "India", + "default_currency": "INR", + "create_chart_of_accounts_based_on": "Standard Template", + "chart_of_accounts": "Standard", + } + ) + company = company.save() + + self.company = company.name def test_service_level_agreement(self): # Default Service Level Agreement @@ -205,7 +227,7 @@ class TestServiceLevelAgreement(unittest.TestCase): # make lead with default SLA creation = datetime.datetime(2019, 3, 4, 12, 0) - lead = make_lead(creation=creation, index=1) + lead = make_lead(creation=creation, index=1, company=self.company) self.assertEqual(lead.service_level_agreement, lead_sla.name) self.assertEqual(lead.response_by, datetime.datetime(2019, 3, 4, 16, 0)) @@ -233,7 +255,7 @@ class TestServiceLevelAgreement(unittest.TestCase): ) creation = datetime.datetime(2020, 3, 4, 4, 0) - lead = make_lead(creation, index=2) + lead = make_lead(creation, index=2, company=self.company) frappe.flags.current_time = datetime.datetime(2020, 3, 4, 4, 15) lead.reload() @@ -267,7 +289,7 @@ class TestServiceLevelAgreement(unittest.TestCase): ) creation = datetime.datetime(2019, 3, 4, 12, 0) - lead = make_lead(creation=creation, index=1) + lead = make_lead(creation=creation, index=1, company=self.company) self.assertEqual(lead.response_by, datetime.datetime(2019, 3, 4, 16, 0)) # failed with response time only @@ -294,7 +316,7 @@ class TestServiceLevelAgreement(unittest.TestCase): # fulfilled with response time only creation = datetime.datetime(2019, 3, 4, 12, 0) - lead = make_lead(creation=creation, index=2) + lead = make_lead(creation=creation, index=2, company=self.company) self.assertEqual(lead.service_level_agreement, lead_sla.name) self.assertEqual(lead.response_by, datetime.datetime(2019, 3, 4, 16, 0)) @@ -321,7 +343,7 @@ class TestServiceLevelAgreement(unittest.TestCase): apply_sla_for_resolution=0, ) creation = datetime.datetime(2019, 3, 4, 12, 0) - lead = make_lead(creation=creation, index=4) + lead = make_lead(creation=creation, index=4, company=self.company) applied_sla = frappe.db.get_value("Lead", lead.name, "service_level_agreement") self.assertFalse(applied_sla) @@ -611,7 +633,7 @@ def create_custom_doctype(): return frappe.get_doc("DocType", "Test SLA on Custom Dt") -def make_lead(creation=None, index=0): +def make_lead(creation=None, index=0, company=None): return frappe.get_doc( { "doctype": "Lead", @@ -621,5 +643,6 @@ def make_lead(creation=None, index=0): "creation": creation, "service_level_agreement_creation": creation, "priority": "Medium", + "company": company, } ).insert(ignore_permissions=True) From e626107d3de7f95f15627a266e652cdfedf020a1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 17 Oct 2022 16:48:13 +0530 Subject: [PATCH 0363/1047] chore: Update allow on submit for Sales Invoice fields --- .../doctype/sales_invoice/sales_invoice.js | 6 ++---- .../doctype/sales_invoice/sales_invoice.py | 20 ++++++++++++------- .../sales_invoice_item.json | 8 ++++++-- .../sales_taxes_and_charges.json | 7 +++++-- erpnext/public/js/controllers/accounts.js | 6 ++++++ 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 7a5d3922f3..435ae9e482 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -76,13 +76,11 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e callback: (r) => { if (!r.exc) { frappe.msgprint(__('Accounting Entries are reposted')); - this.frm.trigger('refresh'); + me.frm.refresh(); } } }); - }); - - $(`["${encodeURIComponent("Repost Accounting Entries")}"]`).css('color', 'red'); + }).removeClass('btn-default').addClass('btn-warning'); } if (this.frm.doc.is_return) { diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 4c38883913..891ae1c4b2 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -522,9 +522,10 @@ class SalesInvoice(SellingController): def on_update_after_submit(self): needs_repost = 0 + # Check if any field affecting accounting entry is altered doc_before_update = self.get_doc_before_save() - accounting_dimensions = get_accounting_dimensions() + accounting_dimensions = get_accounting_dimensions() + ["cost_center", "project"] # Check if opening entry check updated if doc_before_update.get("is_opening") != self.is_opening: @@ -552,12 +553,7 @@ class SalesInvoice(SellingController): # Check for parent level for index, item in enumerate(self.get("items")): - for field in ( - "income_account", - "expense_account", - "discount_account", - "deferred_revenue_account", - ): + for field in ("income_account", "expense_account", "discount_account"): if doc_before_update.get("items")[index].get(field) != item.get(field): needs_repost = 1 break @@ -567,6 +563,16 @@ class SalesInvoice(SellingController): needs_repost = 1 break + for index, tax in enumerate(self.get("taxes")): + if doc_before_update.get("taxes")[index].get("account_head") != tax.get("account_head"): + needs_repost = 1 + break + + for dimension in accounting_dimensions: + if doc_before_update.get("taxes")[index].get(dimension) != tax.get(dimension): + needs_repost = 1 + break + self.validate_accounts() self.db_set("repost_required", needs_repost) diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index a307a6c17c..342a3cc98b 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -436,6 +436,7 @@ "label": "Accounting Details" }, { + "allow_on_submit": 1, "fieldname": "income_account", "fieldtype": "Link", "label": "Income Account", @@ -448,6 +449,7 @@ "width": "120px" }, { + "allow_on_submit": 1, "fieldname": "expense_account", "fieldtype": "Link", "label": "Expense Account", @@ -467,6 +469,7 @@ "print_hide": 1 }, { + "allow_on_submit": 1, "default": ":Company", "fieldname": "cost_center", "fieldtype": "Link", @@ -798,6 +801,7 @@ "options": "Finance Book" }, { + "allow_on_submit": 1, "fieldname": "project", "fieldtype": "Link", "label": "Project", @@ -820,7 +824,6 @@ "label": "Incoming Rate (Costing)", "no_copy": 1, "options": "Company:company:default_currency", - "precision": "6", "print_hide": 1 }, { @@ -833,6 +836,7 @@ "read_only": 1 }, { + "allow_on_submit": 1, "fieldname": "discount_account", "fieldtype": "Link", "label": "Discount Account", @@ -876,7 +880,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-10-10 20:57:38.340026", + "modified": "2022-10-17 12:51:44.825398", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", 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 3a871bfced..e236577e11 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 @@ -51,6 +51,7 @@ "oldfieldtype": "Data" }, { + "allow_on_submit": 1, "columns": 2, "fieldname": "account_head", "fieldtype": "Link", @@ -63,6 +64,7 @@ "search_index": 1 }, { + "allow_on_submit": 1, "default": ":Company", "fieldname": "cost_center", "fieldtype": "Link", @@ -216,12 +218,13 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-08-05 20:04:01.726867", + "modified": "2022-10-17 13:08:17.776528", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges", "owner": "Administrator", "permissions": [], "sort_field": "modified", - "sort_order": "ASC" + "sort_order": "ASC", + "states": [] } \ No newline at end of file diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index c1fe72bb48..a07f75d1c5 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -143,6 +143,12 @@ var get_payment_mode_account = function(frm, mode_of_payment, callback) { cur_frm.cscript.account_head = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; + + if (doc.docstatus == 1) { + // Should not trigger any changes on change post submit + return; + } + if(!d.charge_type && d.account_head){ frappe.msgprint(__("Please select Charge Type first")); frappe.model.set_value(cdt, cdn, "account_head", ""); From 2665d7d293129401ec4737194219a27f7dd7b387 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Mon, 17 Oct 2022 14:29:03 +0200 Subject: [PATCH 0364/1047] feat: page break in SoA pdf --- .../process_statement_of_accounts.js | 4 +++- .../process_statement_of_accounts.json | 10 +++++++++- .../process_statement_of_accounts.py | 9 ++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js index 7dd77fbb3c..7dd5ef36f2 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js @@ -9,6 +9,7 @@ frappe.ui.form.on('Process Statement Of Accounts', { refresh: function(frm){ if(!frm.doc.__islocal) { frm.add_custom_button(__('Send Emails'), function(){ + if (frm.is_dirty()) frappe.throw(__("Please save before proceeding.")) frappe.call({ method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails", args: { @@ -25,7 +26,8 @@ frappe.ui.form.on('Process Statement Of Accounts', { }); }); frm.add_custom_button(__('Download'), function(){ - var url = frappe.urllib.get_full_url( + if (frm.is_dirty()) frappe.throw(__("Please save before proceeding.")) + let url = frappe.urllib.get_full_url( '/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?' + 'document_name='+encodeURIComponent(frm.doc.name)) $.ajax({ diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json index a26267ba5e..83e637077e 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json @@ -27,6 +27,7 @@ "customers", "preferences", "orientation", + "include_break", "include_ageing", "ageing_based_on", "section_break_14", @@ -284,10 +285,16 @@ "fieldtype": "Link", "label": "Terms and Conditions", "options": "Terms and Conditions" + }, + { + "default": "1", + "fieldname": "include_break", + "fieldtype": "Check", + "label": "Page Break After Each SoA" } ], "links": [], - "modified": "2021-09-06 21:00:45.732505", + "modified": "2022-10-17 17:47:08.662475", "modified_by": "Administrator", "module": "Accounts", "name": "Process Statement Of Accounts", @@ -321,5 +328,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index 01f716daa2..2256871e42 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -128,7 +128,8 @@ def get_report_pdf(doc, consolidated=True): if not bool(statement_dict): return False elif consolidated: - result = "".join(list(statement_dict.values())) + delimiter = '
' if doc.include_break else "" + result = delimiter.join(list(statement_dict.values())) return get_pdf(result, {"orientation": doc.orientation}) else: for customer, statement_html in statement_dict.items(): @@ -240,8 +241,8 @@ def fetch_customers(customer_collection, collection_name, primary_mandatory): if int(primary_mandatory): if primary_email == "": continue - elif (billing_email == "") and (primary_email == ""): - continue + # elif (billing_email == "") and (primary_email == ""): + # continue customer_list.append( {"name": customer.name, "primary_email": primary_email, "billing_email": billing_email} @@ -313,6 +314,8 @@ def send_emails(document_name, from_scheduler=False): attachments = [{"fname": customer + ".pdf", "fcontent": report_pdf}] recipients, cc = get_recipients_and_cc(customer, doc) + if not recipients: + continue context = get_context(customer, doc) subject = frappe.render_template(doc.subject, context) message = frappe.render_template(doc.body, context) From 9b50221bf05d1dec20d37dc1e3f4e9f0d4b9945f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 13 Oct 2022 14:13:48 +0530 Subject: [PATCH 0365/1047] refactor: split ple creation function into two refactor create_payment_ledger_entry function into 2. one for generating ple map and one for DB entry creation --- erpnext/accounts/utils.py | 82 ++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 67574ca992..6667454c48 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1368,9 +1368,8 @@ def check_and_delete_linked_reports(report): frappe.delete_doc("Desktop Icon", icon) -def create_payment_ledger_entry( - gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0 -): +def get_payment_ledger_entries(gl_entries, cancel=0): + ple_map = [] if gl_entries: ple = None @@ -1410,44 +1409,57 @@ def create_payment_ledger_entry( dr_or_cr *= -1 dr_or_cr_account_currency *= -1 - ple = frappe.get_doc( - { - "doctype": "Payment Ledger Entry", - "posting_date": gle.posting_date, - "company": gle.company, - "account_type": account_type, - "account": gle.account, - "party_type": gle.party_type, - "party": gle.party, - "cost_center": gle.cost_center, - "finance_book": gle.finance_book, - "due_date": gle.due_date, - "voucher_type": gle.voucher_type, - "voucher_no": gle.voucher_no, - "against_voucher_type": gle.against_voucher_type - if gle.against_voucher_type - else gle.voucher_type, - "against_voucher_no": gle.against_voucher if gle.against_voucher else gle.voucher_no, - "account_currency": gle.account_currency, - "amount": dr_or_cr, - "amount_in_account_currency": dr_or_cr_account_currency, - "delinked": True if cancel else False, - "remarks": gle.remarks, - } + ple = frappe._dict( + doctype="Payment Ledger Entry", + posting_date=gle.posting_date, + company=gle.company, + account_type=account_type, + account=gle.account, + party_type=gle.party_type, + party=gle.party, + cost_center=gle.cost_center, + finance_book=gle.finance_book, + due_date=gle.due_date, + voucher_type=gle.voucher_type, + voucher_no=gle.voucher_no, + against_voucher_type=gle.against_voucher_type + if gle.against_voucher_type + else gle.voucher_type, + against_voucher_no=gle.against_voucher if gle.against_voucher else gle.voucher_no, + account_currency=gle.account_currency, + amount=dr_or_cr, + amount_in_account_currency=dr_or_cr_account_currency, + delinked=True if cancel else False, + remarks=gle.remarks, ) dimensions_and_defaults = get_dimensions() if dimensions_and_defaults: for dimension in dimensions_and_defaults[0]: - ple.set(dimension.fieldname, gle.get(dimension.fieldname)) + ple[dimension.fieldname] = gle.get(dimension.fieldname) - if cancel: - delink_original_entry(ple) - ple.flags.ignore_permissions = 1 - ple.flags.adv_adj = adv_adj - ple.flags.from_repost = from_repost - ple.flags.update_outstanding = update_outstanding - ple.submit() + ple_map.append(ple) + return ple_map + + +def create_payment_ledger_entry( + gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0 +): + if gl_entries: + ple_map = get_payment_ledger_entries(gl_entries, cancel=cancel) + + for entry in ple_map: + + ple = frappe.get_doc(entry) + + if cancel: + delink_original_entry(ple) + + ple.flags.ignore_permissions = 1 + ple.flags.adv_adj = adv_adj + ple.flags.from_repost = from_repost + ple.flags.update_outstanding = update_outstanding + ple.submit() def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, party): From faadf78332a813b69ddb075820cf1693c51d0d48 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 17 Oct 2022 18:50:54 +0530 Subject: [PATCH 0366/1047] fix: Ignore linked purchase invoice on cancel --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 2 +- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index ec861a2787..c3a9855ff4 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -31,7 +31,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. super.onload(); // Ignore linked advances - this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry']; + this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice']; if(!this.frm.doc.__islocal) { // show credit_to in print format diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 5dbe7ebc86..3d74b8f139 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1415,6 +1415,7 @@ class PurchaseInvoice(BuyingController): "Stock Ledger Entry", "Repost Item Valuation", "Payment Ledger Entry", + "Purchase Invoice", ) self.update_advance_tax_references(cancel=1) From 9df99156000af15e1871a2f09922c5f04d0a4ade Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Mon, 17 Oct 2022 15:54:46 +0200 Subject: [PATCH 0367/1047] fix: query condition change --- .../process_statement_of_accounts.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index 2256871e42..48bc3a1bdd 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -6,6 +6,7 @@ import copy import frappe from frappe import _ +from frappe.desk.reportview import get_match_cond from frappe.model.document import Document from frappe.utils import add_days, add_months, format_date, getdate, today from frappe.utils.jinja import validate_template @@ -274,8 +275,12 @@ def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=Tr link.link_doctype='Customer' and link.link_name=%s and contact.is_billing_contact=1 + {mcond} ORDER BY - contact.creation desc""", + contact.creation desc + """.format( + mcond=get_match_cond("Contact") + ), customer_name, ) From 42e4c37f15e2a0613be9104d390bfdc3032f85b7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 17 Oct 2022 20:09:07 +0530 Subject: [PATCH 0368/1047] chore: Break into smaller functions --- .../doctype/sales_invoice/sales_invoice.py | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 891ae1c4b2..1047e88606 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -551,31 +551,38 @@ class SalesInvoice(SellingController): needs_repost = 1 break - # Check for parent level - for index, item in enumerate(self.get("items")): - for field in ("income_account", "expense_account", "discount_account"): - if doc_before_update.get("items")[index].get(field) != item.get(field): - needs_repost = 1 - break + # Check for child tables + if self.check_if_child_table_updated( + "items", + doc_before_update, + ("income_account", "expense_account", "discount_account"), + accounting_dimensions, + ): + needs_repost = 1 - for dimension in accounting_dimensions: - if doc_before_update.get("items")[index].get(dimension) != item.get(dimension): - needs_repost = 1 - break - - for index, tax in enumerate(self.get("taxes")): - if doc_before_update.get("taxes")[index].get("account_head") != tax.get("account_head"): - needs_repost = 1 - break - - for dimension in accounting_dimensions: - if doc_before_update.get("taxes")[index].get(dimension) != tax.get(dimension): - needs_repost = 1 - break + if self.check_if_child_table_updated( + "taxes", doc_before_update, ("account_head",), accounting_dimensions + ): + needs_repost = 1 self.validate_accounts() self.db_set("repost_required", needs_repost) + def check_if_child_table_updated( + self, child_table, doc_before_update, fields_to_check, accounting_dimensions + ): + # Check if any field affecting accounting entry is altered + for index, item in enumerate(self.get(child_table)): + for field in fields_to_check: + if doc_before_update.get(child_table)[index].get(field) != item.get(field): + return True + + for dimension in accounting_dimensions: + if doc_before_update.get(child_table)[index].get(dimension) != item.get(dimension): + return True + + return False + @frappe.whitelist() def repost_accounting_entries(self): self.docstatus = 2 From 43b80683ebd6a902fdb0ea0c4e11a8b2bee9841a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 18 Oct 2022 09:33:37 +0530 Subject: [PATCH 0369/1047] fix: party type and party mandatory on updating outstanding --- erpnext/accounts/utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 67574ca992..ae4ab64dd8 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1467,7 +1467,12 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa # on cancellation outstanding can be an empty list voucher_outstanding = ple_query.get_voucher_outstandings(vouchers, common_filter=common_filter) - if voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"] and voucher_outstanding: + if ( + voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"] + and party_type + and party + and voucher_outstanding + ): outstanding = voucher_outstanding[0] ref_doc = frappe.get_doc(voucher_type, voucher_no) From 8f42e7f7034c6ba63b4eb3a6a122295f98cd04f8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 18 Oct 2022 17:24:36 +0530 Subject: [PATCH 0370/1047] test: use payable account in tax and to trigger party validation --- .../doctype/sales_invoice/test_sales_invoice.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 301d3e136e..46c777d5f1 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3228,6 +3228,22 @@ class TestSalesInvoice(unittest.TestCase): self.assertTrue(return_si.docstatus == 1) + def test_sales_invoice_with_payable_tax_account(self): + si = create_sales_invoice(do_not_submit=True) + si.append( + "taxes", + { + "charge_type": "Actual", + "account_head": "Creditors - _TC", + "description": "Test", + "cost_center": "Main - _TC", + "tax_amount": 10, + "total": 10, + "dont_recompute_tax": 0, + }, + ) + self.assertRaises(frappe.ValidationError, si.submit) + def get_sales_invoice_for_e_invoice(): si = make_sales_invoice_for_ewaybill() From 96b4211ea1cfc9a10ba79b54b5645105f0925943 Mon Sep 17 00:00:00 2001 From: Maharshi Patel <39730881+maharshivpatel@users.noreply.github.com> Date: Tue, 18 Oct 2022 22:26:11 +0530 Subject: [PATCH 0371/1047] fix: pricing rule for non stock UOM and conversions * fix: pricing rule for non stock UOM and conversions --- .../doctype/pricing_rule/pricing_rule.py | 18 ++- .../doctype/pricing_rule/test_pricing_rule.py | 115 ++++++++++++++++++ .../accounts/doctype/pricing_rule/utils.py | 6 + .../sales_invoice/test_sales_invoice.py | 2 +- erpnext/public/js/controllers/transaction.js | 3 + 5 files changed, 142 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 9af3188e47..826d71b12e 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -268,6 +268,18 @@ def get_serial_no_for_item(args): return item_details +def update_pricing_rule_uom(pricing_rule, args): + child_doc = {"Item Code": "items", "Item Group": "item_groups", "Brand": "brands"}.get( + pricing_rule.apply_on + ) + + apply_on_field = frappe.scrub(pricing_rule.apply_on) + + for row in pricing_rule.get(child_doc): + if row.get(apply_on_field) == args.get(apply_on_field): + pricing_rule.uom = row.uom + + def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False): from erpnext.accounts.doctype.pricing_rule.utils import ( get_applied_pricing_rules, @@ -324,6 +336,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa if isinstance(pricing_rule, str): pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule) + update_pricing_rule_uom(pricing_rule, args) pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule) or [] if pricing_rule.get("suggestion"): @@ -440,12 +453,15 @@ def apply_price_discount_rule(pricing_rule, item_details, args): if pricing_rule.currency == args.currency: pricing_rule_rate = pricing_rule.rate + # TODO https://github.com/frappe/erpnext/pull/23636 solve this in some other way. if pricing_rule_rate: + is_blank_uom = pricing_rule.get("uom") != args.get("uom") # Override already set price list rate (from item price) # if pricing_rule_rate > 0 item_details.update( { - "price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1), + "price_list_rate": pricing_rule_rate + * (args.get("conversion_factor", 1) if is_blank_uom else 1), } ) item_details.update({"discount_percentage": 0.0}) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 0a9db6b0f5..fbe567824f 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -595,6 +595,121 @@ class TestPricingRule(unittest.TestCase): frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete() item.delete() + def test_item_price_with_blank_uom_pricing_rule(self): + properties = { + "item_code": "Item Blank UOM", + "stock_uom": "Nos", + "sales_uom": "Box", + "uoms": [dict(uom="Box", conversion_factor=10)], + } + item = make_item(properties=properties) + + make_item_price("Item Blank UOM", "_Test Price List", 100) + + pricing_rule_record = { + "doctype": "Pricing Rule", + "title": "_Test Item Blank UOM Rule", + "apply_on": "Item Code", + "items": [ + { + "item_code": "Item Blank UOM", + } + ], + "selling": 1, + "currency": "INR", + "rate_or_discount": "Rate", + "rate": 101, + "company": "_Test Company", + } + rule = frappe.get_doc(pricing_rule_record) + rule.insert() + + si = create_sales_invoice( + do_not_save=True, item_code="Item Blank UOM", uom="Box", conversion_factor=10 + ) + si.selling_price_list = "_Test Price List" + si.save() + + # If UOM is blank consider it as stock UOM and apply pricing_rule on all UOM. + # rate is 101, Selling UOM is Box that have conversion_factor of 10 so 101 * 10 = 1010 + self.assertEqual(si.items[0].price_list_rate, 1010) + self.assertEqual(si.items[0].rate, 1010) + + si.delete() + + si = create_sales_invoice(do_not_save=True, item_code="Item Blank UOM", uom="Nos") + si.selling_price_list = "_Test Price List" + si.save() + + # UOM is blank so consider it as stock UOM and apply pricing_rule on all UOM. + # rate is 101, Selling UOM is Nos that have conversion_factor of 1 so 101 * 1 = 101 + self.assertEqual(si.items[0].price_list_rate, 101) + self.assertEqual(si.items[0].rate, 101) + + si.delete() + rule.delete() + frappe.get_doc("Item Price", {"item_code": "Item Blank UOM"}).delete() + + item.delete() + + def test_item_price_with_selling_uom_pricing_rule(self): + properties = { + "item_code": "Item UOM other than Stock", + "stock_uom": "Nos", + "sales_uom": "Box", + "uoms": [dict(uom="Box", conversion_factor=10)], + } + item = make_item(properties=properties) + + make_item_price("Item UOM other than Stock", "_Test Price List", 100) + + pricing_rule_record = { + "doctype": "Pricing Rule", + "title": "_Test Item UOM other than Stock Rule", + "apply_on": "Item Code", + "items": [ + { + "item_code": "Item UOM other than Stock", + "uom": "Box", + } + ], + "selling": 1, + "currency": "INR", + "rate_or_discount": "Rate", + "rate": 101, + "company": "_Test Company", + } + rule = frappe.get_doc(pricing_rule_record) + rule.insert() + + si = create_sales_invoice( + do_not_save=True, item_code="Item UOM other than Stock", uom="Box", conversion_factor=10 + ) + si.selling_price_list = "_Test Price List" + si.save() + + # UOM is Box so apply pricing_rule only on Box UOM. + # Selling UOM is Box and as both UOM are same no need to multiply by conversion_factor. + self.assertEqual(si.items[0].price_list_rate, 101) + self.assertEqual(si.items[0].rate, 101) + + si.delete() + + si = create_sales_invoice(do_not_save=True, item_code="Item UOM other than Stock", uom="Nos") + si.selling_price_list = "_Test Price List" + si.save() + + # UOM is Box so pricing_rule won't apply as selling_uom is Nos. + # As Pricing Rule is not applied price of 100 will be fetched from Item Price List. + self.assertEqual(si.items[0].price_list_rate, 100) + self.assertEqual(si.items[0].rate, 100) + + si.delete() + rule.delete() + frappe.get_doc("Item Price", {"item_code": "Item UOM other than Stock"}).delete() + + item.delete() + def test_pricing_rule_for_different_currency(self): make_item("Test Sanitizer Item") diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 1f29d732ba..4c78d7261d 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -111,6 +111,12 @@ def _get_pricing_rules(apply_on, args, values): ) if apply_on_field == "item_code": + if args.get("uom", None): + item_conditions += ( + " and ({child_doc}.uom='{item_uom}' or IFNULL({child_doc}.uom, '')='')".format( + child_doc=child_doc, item_uom=args.get("uom") + ) + ) if "variant_of" not in args: args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of") diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 301d3e136e..8d4ec38c81 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3320,7 +3320,7 @@ def create_sales_invoice(**args): "asset": args.asset or None, "cost_center": args.cost_center or "_Test Cost Center - _TC", "serial_no": args.serial_no, - "conversion_factor": 1, + "conversion_factor": args.get("conversion_factor", 1), "incoming_rate": args.incoming_rate or 0, "batch_no": args.batch_no or None, }, diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index c17610b58a..7fecb18fad 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -426,6 +426,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe if(!this.validate_company_and_party()) { this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove(); } else { + item.pricing_rules = '' return this.frm.call({ method: "erpnext.stock.get_item_details.get_item_details", child: item, @@ -1045,6 +1046,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe uom(doc, cdt, cdn) { var me = this; var item = frappe.get_doc(cdt, cdn); + item.pricing_rules = '' if(item.item_code && item.uom) { return this.frm.call({ method: "erpnext.stock.get_item_details.get_conversion_factor", @@ -1121,6 +1123,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe qty(doc, cdt, cdn) { let item = frappe.get_doc(cdt, cdn); + item.pricing_rules = '' this.conversion_factor(doc, cdt, cdn, true); this.calculate_stock_uom_rate(doc, cdt, cdn); this.apply_pricing_rule(item, true); From 8b2165e0d16401ea24b4e571c12488e3199a48d5 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 19 Oct 2022 14:12:51 +0530 Subject: [PATCH 0372/1047] fix: overlap error not raised for job card in case of workstation with production capacity --- .../doctype/job_card/job_card.py | 2 +- .../doctype/job_card/test_job_card.py | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index ed45106634..fb94e8aa99 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -133,7 +133,7 @@ class JobCard(Document): (%(from_time)s <= jctl.from_time and %(to_time)s >= jctl.to_time) {0} ) and jctl.name != %(name)s and jc.name != %(parent)s and jc.docstatus < 2 {1} - order by jctl.to_time desc limit 1""".format( + order by jctl.to_time desc""".format( extra_cond, validate_overlap_for ), { diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py index ac7114138c..4d2dab73e3 100644 --- a/erpnext/manufacturing/doctype/job_card/test_job_card.py +++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py @@ -136,6 +136,45 @@ class TestJobCard(FrappeTestCase): ) self.assertRaises(OverlapError, jc2.save) + def test_job_card_overlap_with_capacity(self): + wo2 = make_wo_order_test_record(item="_Test FG Item 2", qty=2) + + workstation = make_workstation(workstation_name=random_string(5)).name + frappe.db.set_value("Workstation", workstation, "production_capacity", 1) + + jc1 = frappe.get_last_doc("Job Card", {"work_order": self.work_order.name}) + jc2 = frappe.get_last_doc("Job Card", {"work_order": wo2.name}) + + jc1.workstation = workstation + jc1.append( + "time_logs", + {"from_time": "2021-01-01 00:00:00", "to_time": "2021-01-01 08:00:00", "completed_qty": 1}, + ) + jc1.save() + + jc2.workstation = workstation + + # add a new entry in same time slice + jc2.append( + "time_logs", + {"from_time": "2021-01-01 00:01:00", "to_time": "2021-01-01 06:00:00", "completed_qty": 1}, + ) + self.assertRaises(OverlapError, jc2.save) + + frappe.db.set_value("Workstation", workstation, "production_capacity", 2) + jc2.load_from_db() + + jc2.workstation = workstation + + # add a new entry in same time slice + jc2.append( + "time_logs", + {"from_time": "2021-01-01 00:01:00", "to_time": "2021-01-01 06:00:00", "completed_qty": 1}, + ) + + jc2.save() + self.assertTrue(jc2.name) + def test_job_card_multiple_materials_transfer(self): "Test transferring RMs separately against Job Card with multiple RMs." self.transfer_material_against = "Job Card" From 21d09c5bf23ada46dc943158fa217e05c8e2eb76 Mon Sep 17 00:00:00 2001 From: niralisatapara Date: Wed, 19 Oct 2022 14:33:02 +0530 Subject: [PATCH 0373/1047] Item Wise TDS Calculation --- .../purchase_invoice/purchase_invoice.json | 3109 ++++++++--------- .../tax_withholding_category.py | 13 +- .../test_tax_withholding_category.py | 40 + erpnext/controllers/taxes_and_totals.py | 12 + erpnext/patches.txt | 1 + erpnext/patches/v14_0/update_tds_fields.py | 22 + 6 files changed, 1607 insertions(+), 1590 deletions(-) create mode 100644 erpnext/patches/v14_0/update_tds_fields.py diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index e73d602332..7faf287a92 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1,1586 +1,1525 @@ { - "actions": [], - "allow_import": 1, - "autoname": "naming_series:", - "creation": "2013-05-21 16:16:39", - "doctype": "DocType", - "document_type": "Document", - "engine": "InnoDB", - "field_order": [ - "title", - "naming_series", - "supplier", - "supplier_name", - "tax_id", - "company", - "column_break_6", - "posting_date", - "posting_time", - "set_posting_time", - "due_date", - "column_break1", - "is_paid", - "is_return", - "return_against", - "apply_tds", - "tax_withholding_category", - "amended_from", - "accounting_dimensions_section", - "cost_center", - "dimension_col_break", - "project", - "currency_and_price_list", - "currency", - "conversion_rate", - "column_break2", - "buying_price_list", - "price_list_currency", - "plc_conversion_rate", - "ignore_pricing_rule", - "sec_warehouse", - "scan_barcode", - "col_break_warehouse", - "update_stock", - "set_warehouse", - "set_from_warehouse", - "is_subcontracted", - "rejected_warehouse", - "supplier_warehouse", - "items_section", - "items", - "section_break_26", - "total_qty", - "total_net_weight", - "column_break_50", - "base_total", - "base_net_total", - "column_break_28", - "total", - "net_total", - "taxes_section", - "taxes_and_charges", - "column_break_58", - "tax_category", - "column_break_49", - "shipping_rule", - "section_break_51", - "taxes", - "totals", - "base_taxes_and_charges_added", - "base_taxes_and_charges_deducted", - "base_total_taxes_and_charges", - "column_break_40", - "taxes_and_charges_added", - "taxes_and_charges_deducted", - "total_taxes_and_charges", - "section_break_49", - "base_grand_total", - "base_rounding_adjustment", - "base_rounded_total", - "base_in_words", - "column_break8", - "grand_total", - "rounding_adjustment", - "rounded_total", - "in_words", - "total_advance", - "outstanding_amount", - "disable_rounded_total", - "section_break_44", - "apply_discount_on", - "base_discount_amount", - "additional_discount_account", - "column_break_46", - "additional_discount_percentage", - "discount_amount", - "tax_withheld_vouchers_section", - "tax_withheld_vouchers", - "sec_tax_breakup", - "other_charges_calculation", - "pricing_rule_details", - "pricing_rules", - "raw_materials_supplied", - "supplied_items", - "payments_tab", - "payments_section", - "mode_of_payment", - "base_paid_amount", - "clearance_date", - "col_br_payments", - "cash_bank_account", - "paid_amount", - "advances_section", - "allocate_advances_automatically", - "get_advances", - "advances", - "advance_tax", - "write_off", - "write_off_amount", - "base_write_off_amount", - "column_break_61", - "write_off_account", - "write_off_cost_center", - "address_and_contact_tab", - "section_addresses", - "supplier_address", - "address_display", - "col_break_address", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "company_shipping_address_section", - "shipping_address", - "column_break_126", - "shipping_address_display", - "company_billing_address_section", - "billing_address", - "column_break_130", - "billing_address_display", - "terms_tab", - "payment_schedule_section", - "payment_terms_template", - "ignore_default_payment_terms_template", - "payment_schedule", - "terms_section_break", - "tc_name", - "terms", - "more_info_tab", - "status_section", - "status", - "column_break_177", - "per_received", - "supplier_invoice_details", - "bill_no", - "column_break_15", - "bill_date", - "accounting_details_section", - "credit_to", - "party_account_currency", - "is_opening", - "against_expense_account", - "column_break_63", - "unrealized_profit_loss_account", - "subscription_section", - "auto_repeat", - "update_auto_repeat_reference", - "column_break_114", - "from_date", - "to_date", - "printing_settings", - "letter_head", - "group_same_items", - "column_break_112", - "select_print_heading", - "language", - "sb_14", - "on_hold", - "release_date", - "cb_17", - "hold_comment", - "additional_info_section", - "is_internal_supplier", - "represents_company", - "column_break_147", - "inter_company_invoice_reference", - "is_old_subcontracting_flow", - "remarks", - "connections_tab", - "column_break_38" - ], - "fields": [ - { - "allow_on_submit": 1, - "default": "{supplier_name}", - "fieldname": "title", - "fieldtype": "Data", - "hidden": 1, - "label": "Title", - "no_copy": 1, - "print_hide": 1 - }, - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "no_copy": 1, - "oldfieldname": "naming_series", - "oldfieldtype": "Select", - "options": "ACC-PINV-.YYYY.-\nACC-PINV-RET-.YYYY.-", - "print_hide": 1, - "reqd": 1, - "set_only_once": 1 - }, - { - "fieldname": "supplier", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Supplier", - "oldfieldname": "supplier", - "oldfieldtype": "Link", - "options": "Supplier", - "print_hide": 1, - "reqd": 1, - "search_index": 1 - }, - { - "bold": 1, - "depends_on": "supplier", - "fetch_from": "supplier.supplier_name", - "fieldname": "supplier_name", - "fieldtype": "Data", - "in_global_search": 1, - "label": "Supplier Name", - "oldfieldname": "supplier_name", - "oldfieldtype": "Data", - "read_only": 1 - }, - { - "fetch_from": "supplier.tax_id", - "fieldname": "tax_id", - "fieldtype": "Read Only", - "label": "Tax Id", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "due_date", - "fieldtype": "Date", - "label": "Due Date", - "oldfieldname": "due_date", - "oldfieldtype": "Date" - }, - { - "default": "0", - "fieldname": "is_paid", - "fieldtype": "Check", - "label": "Is Paid", - "print_hide": 1 - }, - { - "default": "0", - "fieldname": "is_return", - "fieldtype": "Check", - "label": "Is Return (Debit Note)", - "no_copy": 1, - "print_hide": 1 - }, - { - "default": "0", - "fieldname": "apply_tds", - "fieldtype": "Check", - "label": "Apply Tax Withholding Amount", - "print_hide": 1 - }, - { - "fieldname": "column_break1", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "width": "50%" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Company", - "options": "Company", - "print_hide": 1, - "remember_last_selected_value": 1 - }, - { - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center" - }, - { - "default": "Today", - "fieldname": "posting_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Date", - "oldfieldname": "posting_date", - "oldfieldtype": "Date", - "print_hide": 1, - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "posting_time", - "fieldtype": "Time", - "label": "Posting Time", - "no_copy": 1, - "print_hide": 1, - "print_width": "100px", - "width": "100px" - }, - { - "default": "0", - "depends_on": "eval:doc.docstatus==0", - "fieldname": "set_posting_time", - "fieldtype": "Check", - "label": "Edit Posting Date and Time", - "print_hide": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Amended From", - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Link", - "options": "Purchase Invoice", - "print_hide": 1, - "read_only": 1 - }, - { - "collapsible": 1, - "collapsible_depends_on": "eval:doc.on_hold", - "fieldname": "sb_14", - "fieldtype": "Section Break", - "label": "Hold Invoice" - }, - { - "default": "0", - "fieldname": "on_hold", - "fieldtype": "Check", - "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" - }, - { - "fieldname": "cb_17", - "fieldtype": "Column Break" - }, - { - "depends_on": "eval:doc.on_hold", - "fieldname": "hold_comment", - "fieldtype": "Small Text", - "label": "Reason For Putting On Hold" - }, - { - "collapsible": 1, - "collapsible_depends_on": "bill_no", - "fieldname": "supplier_invoice_details", - "fieldtype": "Section Break", - "label": "Supplier Invoice" - }, - { - "fieldname": "bill_no", - "fieldtype": "Data", - "label": "Supplier Invoice No", - "oldfieldname": "bill_no", - "oldfieldtype": "Data", - "print_hide": 1 - }, - { - "fieldname": "column_break_15", - "fieldtype": "Column Break" - }, - { - "fieldname": "bill_date", - "fieldtype": "Date", - "label": "Supplier Invoice Date", - "no_copy": 1, - "oldfieldname": "bill_date", - "oldfieldtype": "Date", - "print_hide": 1 - }, - { - "depends_on": "return_against", - "fieldname": "return_against", - "fieldtype": "Link", - "label": "Return Against Purchase Invoice", - "no_copy": 1, - "options": "Purchase Invoice", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "section_addresses", - "fieldtype": "Section Break", - "label": "Supplier Address" - }, - { - "fieldname": "supplier_address", - "fieldtype": "Link", - "label": "Select Supplier Address", - "options": "Address", - "print_hide": 1 - }, - { - "fieldname": "address_display", - "fieldtype": "Small Text", - "label": "Address", - "read_only": 1 - }, - { - "fieldname": "contact_person", - "fieldtype": "Link", - "in_global_search": 1, - "label": "Contact Person", - "options": "Contact", - "print_hide": 1 - }, - { - "fieldname": "contact_display", - "fieldtype": "Small Text", - "label": "Contact", - "read_only": 1 - }, - { - "fieldname": "contact_mobile", - "fieldtype": "Small Text", - "label": "Mobile No", - "read_only": 1 - }, - { - "fieldname": "contact_email", - "fieldtype": "Small Text", - "label": "Contact Email", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "col_break_address", - "fieldtype": "Column Break" - }, - { - "fieldname": "shipping_address", - "fieldtype": "Link", - "label": "Select Shipping Address", - "options": "Address", - "print_hide": 1 - }, - { - "fieldname": "shipping_address_display", - "fieldtype": "Small Text", - "label": "Shipping Address", - "print_hide": 1, - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "currency_and_price_list", - "fieldtype": "Section Break", - "label": "Currency and Price List", - "options": "fa fa-tag" - }, - { - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "oldfieldname": "currency", - "oldfieldtype": "Select", - "options": "Currency", - "print_hide": 1 - }, - { - "fieldname": "conversion_rate", - "fieldtype": "Float", - "label": "Exchange Rate", - "oldfieldname": "conversion_rate", - "oldfieldtype": "Currency", - "precision": "9", - "print_hide": 1 - }, - { - "fieldname": "column_break2", - "fieldtype": "Column Break" - }, - { - "fieldname": "buying_price_list", - "fieldtype": "Link", - "label": "Price List", - "options": "Price List", - "print_hide": 1 - }, - { - "fieldname": "price_list_currency", - "fieldtype": "Link", - "label": "Price List Currency", - "options": "Currency", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "plc_conversion_rate", - "fieldtype": "Float", - "label": "Price List Exchange Rate", - "precision": "9", - "print_hide": 1 - }, - { - "default": "0", - "fieldname": "ignore_pricing_rule", - "fieldtype": "Check", - "label": "Ignore Pricing Rule", - "permlevel": 1, - "print_hide": 1 - }, - { - "fieldname": "sec_warehouse", - "fieldtype": "Section Break", - "hide_border": 1, - "label": "Items" - }, - { - "depends_on": "update_stock", - "fieldname": "set_warehouse", - "fieldtype": "Link", - "label": "Set Accepted Warehouse", - "options": "Warehouse", - "print_hide": 1 - }, - { - "depends_on": "update_stock", - "fieldname": "rejected_warehouse", - "fieldtype": "Link", - "label": "Rejected Warehouse", - "no_copy": 1, - "options": "Warehouse", - "print_hide": 1 - }, - { - "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "is_subcontracted", - "fieldtype": "Check", - "label": "Is Subcontracted", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "items_section", - "fieldtype": "Section Break", - "hide_border": 1, - "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" - }, - { - "default": "0", - "fieldname": "update_stock", - "fieldtype": "Check", - "label": "Update Stock", - "print_hide": 1 - }, - { - "fieldname": "scan_barcode", - "fieldtype": "Data", - "label": "Scan Barcode", - "options": "Barcode" - }, - { - "allow_bulk_edit": 1, - "fieldname": "items", - "fieldtype": "Table", - "label": "Items", - "oldfieldname": "entries", - "oldfieldtype": "Table", - "options": "Purchase Invoice Item", - "reqd": 1 - }, - { - "collapsible": 1, - "fieldname": "pricing_rule_details", - "fieldtype": "Section Break", - "label": "Pricing Rules" - }, - { - "fieldname": "pricing_rules", - "fieldtype": "Table", - "label": "Pricing Rule Detail", - "options": "Pricing Rule Detail", - "read_only": 1 - }, - { - "collapsible": 1, - "collapsible_depends_on": "supplied_items", - "fieldname": "raw_materials_supplied", - "fieldtype": "Section Break", - "label": "Raw Materials Supplied" - }, - { - "depends_on": "update_stock", - "fieldname": "supplied_items", - "fieldtype": "Table", - "label": "Supplied Items", - "no_copy": 1, - "options": "Purchase Receipt Item Supplied" - }, - { - "fieldname": "section_break_26", - "fieldtype": "Section Break" - }, - { - "fieldname": "total_qty", - "fieldtype": "Float", - "label": "Total Quantity", - "read_only": 1 - }, - { - "fieldname": "base_total", - "fieldtype": "Currency", - "label": "Total (Company Currency)", - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "base_net_total", - "fieldtype": "Currency", - "label": "Net Total (Company Currency)", - "oldfieldname": "net_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "column_break_28", - "fieldtype": "Column Break" - }, - { - "fieldname": "total", - "fieldtype": "Currency", - "label": "Total", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "net_total", - "fieldtype": "Currency", - "label": "Net Total", - "oldfieldname": "net_total_import", - "oldfieldtype": "Currency", - "options": "currency", - "print_hide": 1, - "read_only": 1 - }, - { - "depends_on": "total_net_weight", - "fieldname": "total_net_weight", - "fieldtype": "Float", - "label": "Total Net Weight", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "taxes_section", - "fieldtype": "Section Break", - "hide_border": 1, - "label": "Taxes and Charges", - "oldfieldtype": "Section Break", - "options": "fa fa-money" - }, - { - "fieldname": "tax_category", - "fieldtype": "Link", - "label": "Tax Category", - "options": "Tax Category", - "print_hide": 1 - }, - { - "fieldname": "column_break_49", - "fieldtype": "Column Break" - }, - { - "fieldname": "shipping_rule", - "fieldtype": "Link", - "label": "Shipping Rule", - "options": "Shipping Rule", - "print_hide": 1 - }, - { - "fieldname": "section_break_51", - "fieldtype": "Section Break", - "hide_border": 1 - }, - { - "fieldname": "taxes_and_charges", - "fieldtype": "Link", - "label": "Purchase Taxes and Charges Template", - "oldfieldname": "purchase_other_charges", - "oldfieldtype": "Link", - "options": "Purchase Taxes and Charges Template", - "print_hide": 1 - }, - { - "fieldname": "taxes", - "fieldtype": "Table", - "label": "Purchase Taxes and Charges", - "oldfieldname": "purchase_tax_details", - "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" - }, - { - "collapsible": 1, - "fieldname": "sec_tax_breakup", - "fieldtype": "Section Break", - "label": "Tax Breakup" - }, - { - "fieldname": "other_charges_calculation", - "fieldtype": "Long Text", - "label": "Taxes and Charges Calculation", - "no_copy": 1, - "oldfieldtype": "HTML", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "totals", - "fieldtype": "Section Break", - "oldfieldtype": "Section Break", - "options": "fa fa-money" - }, - { - "fieldname": "base_taxes_and_charges_added", - "fieldtype": "Currency", - "label": "Taxes and Charges Added (Company Currency)", - "oldfieldname": "other_charges_added", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "base_taxes_and_charges_deducted", - "fieldtype": "Currency", - "label": "Taxes and Charges Deducted (Company Currency)", - "oldfieldname": "other_charges_deducted", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "base_total_taxes_and_charges", - "fieldtype": "Currency", - "label": "Total Taxes and Charges (Company Currency)", - "oldfieldname": "total_tax", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "column_break_40", - "fieldtype": "Column Break" - }, - { - "fieldname": "taxes_and_charges_added", - "fieldtype": "Currency", - "label": "Taxes and Charges Added", - "oldfieldname": "other_charges_added_import", - "oldfieldtype": "Currency", - "options": "currency", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "taxes_and_charges_deducted", - "fieldtype": "Currency", - "label": "Taxes and Charges Deducted", - "oldfieldname": "other_charges_deducted_import", - "oldfieldtype": "Currency", - "options": "currency", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "total_taxes_and_charges", - "fieldtype": "Currency", - "label": "Total Taxes and Charges", - "options": "currency", - "print_hide": 1, - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "section_break_44", - "fieldtype": "Section Break", - "label": "Additional Discount" - }, - { - "default": "Grand Total", - "fieldname": "apply_discount_on", - "fieldtype": "Select", - "label": "Apply Additional Discount On", - "options": "\nGrand Total\nNet Total", - "print_hide": 1 - }, - { - "fieldname": "base_discount_amount", - "fieldtype": "Currency", - "label": "Additional Discount Amount (Company Currency)", - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "column_break_46", - "fieldtype": "Column Break" - }, - { - "fieldname": "additional_discount_percentage", - "fieldtype": "Float", - "label": "Additional Discount Percentage", - "print_hide": 1 - }, - { - "fieldname": "discount_amount", - "fieldtype": "Currency", - "label": "Additional Discount Amount", - "options": "currency", - "print_hide": 1 - }, - { - "fieldname": "section_break_49", - "fieldtype": "Section Break", - "label": "Totals" - }, - { - "fieldname": "base_grand_total", - "fieldtype": "Currency", - "label": "Grand Total (Company Currency)", - "oldfieldname": "grand_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 1 - }, - { - "depends_on": "eval:!doc.disable_rounded_total", - "fieldname": "base_rounding_adjustment", - "fieldtype": "Currency", - "label": "Rounding Adjustment (Company Currency)", - "no_copy": 1, - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 1 - }, - { - "depends_on": "eval:!doc.disable_rounded_total", - "fieldname": "base_rounded_total", - "fieldtype": "Currency", - "label": "Rounded Total (Company Currency)", - "no_copy": 1, - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "base_in_words", - "fieldtype": "Data", - "label": "In Words (Company Currency)", - "length": 240, - "oldfieldname": "in_words", - "oldfieldtype": "Data", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "column_break8", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "print_hide": 1, - "width": "50%" - }, - { - "fieldname": "grand_total", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Grand Total", - "oldfieldname": "grand_total_import", - "oldfieldtype": "Currency", - "options": "currency", - "read_only": 1 - }, - { - "depends_on": "eval:!doc.disable_rounded_total", - "fieldname": "rounding_adjustment", - "fieldtype": "Currency", - "label": "Rounding Adjustment", - "no_copy": 1, - "options": "currency", - "print_hide": 1, - "read_only": 1 - }, - { - "depends_on": "eval:!doc.disable_rounded_total", - "fieldname": "rounded_total", - "fieldtype": "Currency", - "label": "Rounded Total", - "no_copy": 1, - "options": "currency", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "in_words", - "fieldtype": "Data", - "label": "In Words", - "length": 240, - "oldfieldname": "in_words_import", - "oldfieldtype": "Data", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "total_advance", - "fieldtype": "Currency", - "label": "Total Advance", - "no_copy": 1, - "oldfieldname": "total_advance", - "oldfieldtype": "Currency", - "options": "party_account_currency", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "outstanding_amount", - "fieldtype": "Currency", - "label": "Outstanding Amount", - "no_copy": 1, - "oldfieldname": "outstanding_amount", - "oldfieldtype": "Currency", - "options": "party_account_currency", - "print_hide": 1, - "read_only": 1 - }, - { - "default": "0", - "depends_on": "grand_total", - "fieldname": "disable_rounded_total", - "fieldtype": "Check", - "label": "Disable Rounded Total" - }, - { - "collapsible": 1, - "collapsible_depends_on": "paid_amount", - "depends_on": "eval:doc.is_paid===1||(doc.advances && doc.advances.length>0)", - "fieldname": "payments_section", - "fieldtype": "Section Break", - "label": "Payments" - }, - { - "fieldname": "mode_of_payment", - "fieldtype": "Link", - "label": "Mode of Payment", - "options": "Mode of Payment", - "print_hide": 1 - }, - { - "fieldname": "cash_bank_account", - "fieldtype": "Link", - "label": "Cash/Bank Account", - "options": "Account" - }, - { - "fieldname": "clearance_date", - "fieldtype": "Date", - "label": "Clearance Date", - "no_copy": 1, - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "col_br_payments", - "fieldtype": "Column Break" - }, - { - "depends_on": "is_paid", - "fieldname": "paid_amount", - "fieldtype": "Currency", - "label": "Paid Amount", - "no_copy": 1, - "options": "currency", - "print_hide": 1 - }, - { - "fieldname": "base_paid_amount", - "fieldtype": "Currency", - "label": "Paid Amount (Company Currency)", - "no_copy": 1, - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "write_off", - "fieldtype": "Section Break", - "label": "Write Off" - }, - { - "fieldname": "write_off_amount", - "fieldtype": "Currency", - "label": "Write Off Amount", - "no_copy": 1, - "options": "currency", - "print_hide": 1 - }, - { - "fieldname": "base_write_off_amount", - "fieldtype": "Currency", - "label": "Write Off Amount (Company Currency)", - "no_copy": 1, - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "column_break_61", - "fieldtype": "Column Break" - }, - { - "depends_on": "eval:flt(doc.write_off_amount)!=0", - "fieldname": "write_off_account", - "fieldtype": "Link", - "label": "Write Off Account", - "options": "Account", - "print_hide": 1 - }, - { - "depends_on": "eval:flt(doc.write_off_amount)!=0", - "fieldname": "write_off_cost_center", - "fieldtype": "Link", - "label": "Write Off Cost Center", - "options": "Cost Center", - "print_hide": 1 - }, - { - "collapsible": 1, - "collapsible_depends_on": "advances", - "fieldname": "advances_section", - "fieldtype": "Section Break", - "label": "Advance Payments", - "oldfieldtype": "Section Break", - "options": "fa fa-money", - "print_hide": 1 - }, - { - "default": "0", - "fieldname": "allocate_advances_automatically", - "fieldtype": "Check", - "label": "Set Advances and Allocate (FIFO)" - }, - { - "depends_on": "eval:!doc.allocate_advances_automatically", - "fieldname": "get_advances", - "fieldtype": "Button", - "label": "Get Advances Paid", - "oldfieldtype": "Button", - "print_hide": 1 - }, - { - "fieldname": "advances", - "fieldtype": "Table", - "label": "Advances", - "no_copy": 1, - "oldfieldname": "advance_allocation_details", - "oldfieldtype": "Table", - "options": "Purchase Invoice Advance", - "print_hide": 1 - }, - { - "collapsible_depends_on": "eval:(!doc.is_return)", - "fieldname": "payment_schedule_section", - "fieldtype": "Section Break", - "label": "Payment Terms" - }, - { - "fieldname": "payment_terms_template", - "fieldtype": "Link", - "label": "Payment Terms Template", - "options": "Payment Terms Template" - }, - { - "fieldname": "payment_schedule", - "fieldtype": "Table", - "label": "Payment Schedule", - "no_copy": 1, - "options": "Payment Schedule", - "print_hide": 1 - }, - { - "fieldname": "terms_section_break", - "fieldtype": "Section Break", - "label": "Terms and Conditions", - "options": "fa fa-legal" - }, - { - "fieldname": "tc_name", - "fieldtype": "Link", - "label": "Terms", - "options": "Terms and Conditions", - "print_hide": 1 - }, - { - "fieldname": "terms", - "fieldtype": "Text Editor", - "label": "Terms and Conditions" - }, - { - "collapsible": 1, - "fieldname": "printing_settings", - "fieldtype": "Section Break", - "label": "Print Settings" - }, - { - "allow_on_submit": 1, - "fieldname": "letter_head", - "fieldtype": "Link", - "label": "Letter Head", - "options": "Letter Head", - "print_hide": 1 - }, - { - "allow_on_submit": 1, - "default": "0", - "fieldname": "group_same_items", - "fieldtype": "Check", - "label": "Group same items", - "print_hide": 1 - }, - { - "fieldname": "column_break_112", - "fieldtype": "Column Break" - }, - { - "allow_on_submit": 1, - "fieldname": "select_print_heading", - "fieldtype": "Link", - "label": "Print Heading", - "no_copy": 1, - "oldfieldname": "select_print_heading", - "oldfieldtype": "Link", - "options": "Print Heading", - "print_hide": 1, - "report_hide": 1 - }, - { - "fieldname": "language", - "fieldtype": "Data", - "label": "Print Language", - "print_hide": 1, - "read_only": 1 - }, - { - "default": "0", - "fetch_from": "supplier.is_internal_supplier", - "fieldname": "is_internal_supplier", - "fieldtype": "Check", - "ignore_user_permissions": 1, - "label": "Is Internal Supplier", - "read_only": 1 - }, - { - "fieldname": "credit_to", - "fieldtype": "Link", - "label": "Credit To", - "oldfieldname": "credit_to", - "oldfieldtype": "Link", - "options": "Account", - "print_hide": 1, - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "party_account_currency", - "fieldtype": "Link", - "hidden": 1, - "label": "Party Account Currency", - "no_copy": 1, - "options": "Currency", - "print_hide": 1, - "read_only": 1 - }, - { - "default": "No", - "fieldname": "is_opening", - "fieldtype": "Select", - "label": "Is Opening Entry", - "oldfieldname": "is_opening", - "oldfieldtype": "Select", - "options": "No\nYes", - "print_hide": 1 - }, - { - "fieldname": "against_expense_account", - "fieldtype": "Small Text", - "hidden": 1, - "label": "Against Expense Account", - "no_copy": 1, - "oldfieldname": "against_expense_account", - "oldfieldtype": "Small Text", - "print_hide": 1 - }, - { - "fieldname": "column_break_63", - "fieldtype": "Column Break" - }, - { - "default": "Draft", - "fieldname": "status", - "fieldtype": "Select", - "in_standard_filter": 1, - "label": "Status", - "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nPartly Paid\nUnpaid\nOverdue\nCancelled\nInternal Transfer", - "print_hide": 1 - }, - { - "fieldname": "inter_company_invoice_reference", - "fieldtype": "Link", - "label": "Inter Company Invoice Reference", - "no_copy": 1, - "options": "Sales Invoice", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "remarks", - "fieldtype": "Small Text", - "label": "Remarks", - "no_copy": 1, - "oldfieldname": "remarks", - "oldfieldtype": "Text", - "print_hide": 1 - }, - { - "collapsible": 1, - "fieldname": "subscription_section", - "fieldtype": "Section Break", - "label": "Subscription", - "print_hide": 1 - }, - { - "allow_on_submit": 1, - "description": "Start date of current invoice's period", - "fieldname": "from_date", - "fieldtype": "Date", - "label": "From Date", - "no_copy": 1, - "print_hide": 1 - }, - { - "allow_on_submit": 1, - "description": "End date of current invoice's period", - "fieldname": "to_date", - "fieldtype": "Date", - "label": "To Date", - "no_copy": 1, - "print_hide": 1 - }, - { - "fieldname": "column_break_114", - "fieldtype": "Column Break" - }, - { - "fieldname": "auto_repeat", - "fieldtype": "Link", - "label": "Auto Repeat", - "no_copy": 1, - "options": "Auto Repeat", - "print_hide": 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" - }, - { - "collapsible": 1, - "fieldname": "accounting_dimensions_section", - "fieldtype": "Section Break", - "label": "Accounting Dimensions " - }, - { - "fieldname": "dimension_col_break", - "fieldtype": "Column Break" - }, - { - "fieldname": "tax_withholding_category", - "fieldtype": "Link", - "hidden": 1, - "label": "Tax Withholding Category", - "options": "Tax Withholding Category", - "print_hide": 1 - }, - { - "fieldname": "billing_address", - "fieldtype": "Link", - "label": "Select Billing Address", - "options": "Address" - }, - { - "fieldname": "billing_address_display", - "fieldtype": "Small Text", - "label": "Billing Address", - "read_only": 1 - }, - { - "fieldname": "project", - "fieldtype": "Link", - "label": "Project", - "options": "Project" - }, - { - "depends_on": "eval:doc.is_internal_supplier", - "description": "Unrealized Profit/Loss account for intra-company transfers", - "fieldname": "unrealized_profit_loss_account", - "fieldtype": "Link", - "label": "Unrealized Profit / Loss Account", - "options": "Account" - }, - { - "depends_on": "eval:doc.is_internal_supplier", - "description": "Company which internal supplier represents", - "fetch_from": "supplier.represents_company", - "fieldname": "represents_company", - "fieldtype": "Link", - "label": "Represents Company", - "options": "Company" - }, - { - "depends_on": "eval:doc.update_stock && doc.is_internal_supplier", - "fieldname": "set_from_warehouse", - "fieldtype": "Link", - "label": "Set From Warehouse", - "no_copy": 1, - "options": "Warehouse", - "print_hide": 1, - "print_width": "50px", - "width": "50px" - }, - { - "depends_on": "eval:doc.is_subcontracted", - "fieldname": "supplier_warehouse", - "fieldtype": "Link", - "label": "Supplier Warehouse", - "no_copy": 1, - "options": "Warehouse", - "print_hide": 1, - "print_width": "50px", - "width": "50px" - }, - { - "fieldname": "per_received", - "fieldtype": "Percent", - "hidden": 1, - "label": "Per Received", - "no_copy": 1, - "print_hide": 1, - "read_only": 1 - }, - { - "default": "0", - "fieldname": "ignore_default_payment_terms_template", - "fieldtype": "Check", - "hidden": 1, - "label": "Ignore Default Payment Terms Template", - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "accounting_details_section", - "fieldtype": "Section Break", - "label": "Accounting Details", - "print_hide": 1 - }, - { - "fieldname": "column_break_147", - "fieldtype": "Column Break" - }, - { - "fieldname": "advance_tax", - "fieldtype": "Table", - "hidden": 1, - "label": "Advance Tax", - "options": "Advance Tax", - "read_only": 1 - }, - { - "default": "0", - "fieldname": "is_old_subcontracting_flow", - "fieldtype": "Check", - "hidden": 1, - "label": "Is Old Subcontracting Flow", - "read_only": 1 - }, - { - "collapsible_depends_on": "tax_withheld_vouchers", - "fieldname": "tax_withheld_vouchers_section", - "fieldtype": "Section Break", - "label": "Tax Withheld Vouchers" - }, - { - "fieldname": "tax_withheld_vouchers", - "fieldtype": "Table", - "label": "Tax Withheld Vouchers", - "no_copy": 1, - "options": "Tax Withheld Vouchers", - "read_only": 1 - }, - { - "fieldname": "payments_tab", - "fieldtype": "Tab Break", - "label": "Payments" - }, - { - "fieldname": "address_and_contact_tab", - "fieldtype": "Tab Break", - "label": "Address & Contact" - }, - { - "fieldname": "terms_tab", - "fieldtype": "Tab Break", - "label": "Terms" - }, - { - "fieldname": "more_info_tab", - "fieldtype": "Tab Break", - "label": "More Info" - }, - { - "fieldname": "connections_tab", - "fieldtype": "Tab Break", - "label": "Connections", - "show_dashboard": 1 - }, - { - "fieldname": "column_break_6", - "fieldtype": "Column Break" - }, - { - "fieldname": "column_break_38", - "fieldtype": "Column Break" - }, - { - "fieldname": "column_break_50", - "fieldtype": "Column Break" - }, - { - "fieldname": "column_break_58", - "fieldtype": "Column Break" - }, - { - "fieldname": "company_shipping_address_section", - "fieldtype": "Section Break", - "label": "Company Shipping Address" - }, - { - "fieldname": "column_break_126", - "fieldtype": "Column Break" - }, - { - "fieldname": "company_billing_address_section", - "fieldtype": "Section Break", - "label": "Company Billing Address" - }, - { - "fieldname": "column_break_130", - "fieldtype": "Column Break" - }, - { - "collapsible": 1, - "fieldname": "status_section", - "fieldtype": "Section Break", - "label": "Status" - }, - { - "fieldname": "column_break_177", - "fieldtype": "Column Break" - }, - { - "collapsible": 1, - "fieldname": "additional_info_section", - "fieldtype": "Section Break", - "label": "Additional Info", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "print_hide": 1 - } - ], - "icon": "fa fa-file-text", - "idx": 204, - "is_submittable": 1, - "links": [], - "modified": "2022-10-11 13:04:44.304389", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Purchase Invoice", - "name_case": "Title Case", - "naming_rule": "By \"Naming Series\" field", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase User" - }, - { - "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 - }, - { - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Auditor" - }, - { - "permlevel": 1, - "read": 1, - "role": "Accounts Manager", - "write": 1 - } - ], - "search_fields": "posting_date, supplier, bill_no, base_grand_total, outstanding_amount", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "timeline_field": "supplier", - "title_field": "title", - "track_changes": 1 -} \ No newline at end of file + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-05-21 16:16:39", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "title", + "naming_series", + "supplier", + "supplier_name", + "tax_id", + "due_date", + "tax_withholding_category", + "column_break1", + "company", + "posting_date", + "posting_time", + "set_posting_time", + "is_paid", + "is_return", + "apply_tds", + "amended_from", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break", + "project", + "supplier_invoice_details", + "bill_no", + "column_break_15", + "bill_date", + "returns", + "return_against", + "section_addresses", + "supplier_address", + "address_display", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "col_break_address", + "shipping_address", + "shipping_address_display", + "billing_address", + "billing_address_display", + "currency_and_price_list", + "currency", + "conversion_rate", + "column_break2", + "buying_price_list", + "price_list_currency", + "plc_conversion_rate", + "ignore_pricing_rule", + "sec_warehouse", + "set_warehouse", + "rejected_warehouse", + "col_break_warehouse", + "set_from_warehouse", + "supplier_warehouse", + "is_subcontracted", + "items_section", + "update_stock", + "scan_barcode", + "items", + "pricing_rule_details", + "pricing_rules", + "raw_materials_supplied", + "supplied_items", + "section_break_26", + "total_qty", + "base_total", + "base_net_total", + "column_break_28", + "total_net_weight", + "total", + "net_total", + "tax_withholding_net_total", + "base_tax_withholding_net_total", + "taxes_section", + "tax_category", + "column_break_49", + "shipping_rule", + "section_break_51", + "taxes_and_charges", + "taxes", + "tax_withheld_vouchers_section", + "tax_withheld_vouchers", + "sec_tax_breakup", + "other_charges_calculation", + "totals", + "base_taxes_and_charges_added", + "base_taxes_and_charges_deducted", + "base_total_taxes_and_charges", + "column_break_40", + "taxes_and_charges_added", + "taxes_and_charges_deducted", + "total_taxes_and_charges", + "section_break_44", + "apply_discount_on", + "base_discount_amount", + "column_break_46", + "additional_discount_percentage", + "discount_amount", + "section_break_49", + "base_grand_total", + "base_rounding_adjustment", + "base_rounded_total", + "base_in_words", + "column_break8", + "grand_total", + "rounding_adjustment", + "rounded_total", + "in_words", + "total_advance", + "outstanding_amount", + "disable_rounded_total", + "payments_section", + "mode_of_payment", + "cash_bank_account", + "clearance_date", + "col_br_payments", + "paid_amount", + "base_paid_amount", + "write_off", + "write_off_amount", + "base_write_off_amount", + "column_break_61", + "write_off_account", + "write_off_cost_center", + "advances_section", + "allocate_advances_automatically", + "get_advances", + "advances", + "advance_tax", + "payment_schedule_section", + "payment_terms_template", + "ignore_default_payment_terms_template", + "payment_schedule", + "terms_section_break", + "tc_name", + "terms", + "printing_settings", + "letter_head", + "select_print_heading", + "column_break_112", + "group_same_items", + "language", + "sb_14", + "on_hold", + "release_date", + "cb_17", + "hold_comment", + "more_info", + "status", + "inter_company_invoice_reference", + "represents_company", + "column_break_147", + "is_internal_supplier", + "accounting_details_section", + "credit_to", + "party_account_currency", + "is_opening", + "against_expense_account", + "column_break_63", + "unrealized_profit_loss_account", + "remarks", + "subscription_section", + "from_date", + "to_date", + "column_break_114", + "auto_repeat", + "update_auto_repeat_reference", + "per_received", + "is_old_subcontracting_flow" + ], + "fields": [ + { + "allow_on_submit": 1, + "default": "{supplier_name}", + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "no_copy": 1, + "print_hide": 1 + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "oldfieldname": "naming_series", + "oldfieldtype": "Select", + "options": "ACC-PINV-.YYYY.-\nACC-PINV-RET-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "supplier", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Supplier", + "oldfieldname": "supplier", + "oldfieldtype": "Link", + "options": "Supplier", + "print_hide": 1, + "reqd": 1, + "search_index": 1 + }, + { + "bold": 1, + "depends_on": "supplier", + "fetch_from": "supplier.supplier_name", + "fieldname": "supplier_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Supplier Name", + "oldfieldname": "supplier_name", + "oldfieldtype": "Data", + "read_only": 1 + }, + { + "fetch_from": "supplier.tax_id", + "fieldname": "tax_id", + "fieldtype": "Read Only", + "label": "Tax Id", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "due_date", + "fieldtype": "Date", + "label": "Due Date", + "oldfieldname": "due_date", + "oldfieldtype": "Date" + }, + { + "default": "0", + "fieldname": "is_paid", + "fieldtype": "Check", + "label": "Is Paid", + "print_hide": 1 + }, + { + "default": "0", + "fieldname": "is_return", + "fieldtype": "Check", + "label": "Is Return (Debit Note)", + "no_copy": 1, + "print_hide": 1 + }, + { + "default": "0", + "fieldname": "apply_tds", + "fieldtype": "Check", + "label": "Apply Tax Withholding Amount", + "print_hide": 1 + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company", + "print_hide": 1, + "remember_last_selected_value": 1 + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Date", + "oldfieldname": "posting_date", + "oldfieldtype": "Date", + "print_hide": 1, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "posting_time", + "fieldtype": "Time", + "label": "Posting Time", + "no_copy": 1, + "print_hide": 1, + "print_width": "100px", + "width": "100px" + }, + { + "default": "0", + "depends_on": "eval:doc.docstatus==0", + "fieldname": "set_posting_time", + "fieldtype": "Check", + "label": "Edit Posting Date and Time", + "print_hide": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Link", + "options": "Purchase Invoice", + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:doc.on_hold", + "fieldname": "sb_14", + "fieldtype": "Section Break", + "label": "Hold Invoice" + }, + { + "default": "0", + "fieldname": "on_hold", + "fieldtype": "Check", + "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" + }, + { + "fieldname": "cb_17", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:doc.on_hold", + "fieldname": "hold_comment", + "fieldtype": "Small Text", + "label": "Reason For Putting On Hold" + }, + { + "collapsible": 1, + "collapsible_depends_on": "bill_no", + "fieldname": "supplier_invoice_details", + "fieldtype": "Section Break", + "label": "Supplier Invoice Details" + }, + { + "fieldname": "bill_no", + "fieldtype": "Data", + "label": "Supplier Invoice No", + "oldfieldname": "bill_no", + "oldfieldtype": "Data", + "print_hide": 1 + }, + { + "fieldname": "column_break_15", + "fieldtype": "Column Break" + }, + { + "fieldname": "bill_date", + "fieldtype": "Date", + "label": "Supplier Invoice Date", + "no_copy": 1, + "oldfieldname": "bill_date", + "oldfieldtype": "Date", + "print_hide": 1 + }, + { + "depends_on": "return_against", + "fieldname": "returns", + "fieldtype": "Section Break", + "label": "Returns" + }, + { + "depends_on": "return_against", + "fieldname": "return_against", + "fieldtype": "Link", + "label": "Return Against Purchase Invoice", + "no_copy": 1, + "options": "Purchase Invoice", + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "section_addresses", + "fieldtype": "Section Break", + "label": "Address and Contact" + }, + { + "fieldname": "supplier_address", + "fieldtype": "Link", + "label": "Select Supplier Address", + "options": "Address", + "print_hide": 1 + }, + { + "fieldname": "address_display", + "fieldtype": "Small Text", + "label": "Address", + "read_only": 1 + }, + { + "fieldname": "contact_person", + "fieldtype": "Link", + "in_global_search": 1, + "label": "Contact Person", + "options": "Contact", + "print_hide": 1 + }, + { + "fieldname": "contact_display", + "fieldtype": "Small Text", + "label": "Contact", + "read_only": 1 + }, + { + "fieldname": "contact_mobile", + "fieldtype": "Small Text", + "label": "Mobile No", + "read_only": 1 + }, + { + "fieldname": "contact_email", + "fieldtype": "Small Text", + "label": "Contact Email", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "col_break_address", + "fieldtype": "Column Break" + }, + { + "fieldname": "shipping_address", + "fieldtype": "Link", + "label": "Select Shipping Address", + "options": "Address", + "print_hide": 1 + }, + { + "fieldname": "shipping_address_display", + "fieldtype": "Small Text", + "label": "Shipping Address", + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "currency_and_price_list", + "fieldtype": "Section Break", + "label": "Currency and Price List", + "options": "fa fa-tag" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "oldfieldname": "currency", + "oldfieldtype": "Select", + "options": "Currency", + "print_hide": 1 + }, + { + "fieldname": "conversion_rate", + "fieldtype": "Float", + "label": "Exchange Rate", + "oldfieldname": "conversion_rate", + "oldfieldtype": "Currency", + "precision": "9", + "print_hide": 1 + }, + { + "fieldname": "column_break2", + "fieldtype": "Column Break" + }, + { + "fieldname": "buying_price_list", + "fieldtype": "Link", + "label": "Price List", + "options": "Price List", + "print_hide": 1 + }, + { + "fieldname": "price_list_currency", + "fieldtype": "Link", + "label": "Price List Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "plc_conversion_rate", + "fieldtype": "Float", + "label": "Price List Exchange Rate", + "precision": "9", + "print_hide": 1 + }, + { + "default": "0", + "fieldname": "ignore_pricing_rule", + "fieldtype": "Check", + "label": "Ignore Pricing Rule", + "permlevel": 1, + "print_hide": 1 + }, + { + "fieldname": "sec_warehouse", + "fieldtype": "Section Break" + }, + { + "depends_on": "update_stock", + "description": "Sets 'Accepted Warehouse' in each row of the items table.", + "fieldname": "set_warehouse", + "fieldtype": "Link", + "label": "Set Accepted Warehouse", + "options": "Warehouse", + "print_hide": 1 + }, + { + "depends_on": "update_stock", + "description": "Warehouse where you are maintaining stock of rejected items", + "fieldname": "rejected_warehouse", + "fieldtype": "Link", + "label": "Rejected Warehouse", + "no_copy": 1, + "options": "Warehouse", + "print_hide": 1 + }, + { + "fieldname": "col_break_warehouse", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "is_subcontracted", + "fieldtype": "Check", + "label": "Is Subcontracted", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "items_section", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "fa fa-shopping-cart" + }, + { + "default": "0", + "fieldname": "update_stock", + "fieldtype": "Check", + "label": "Update Stock", + "print_hide": 1 + }, + { + "fieldname": "scan_barcode", + "fieldtype": "Data", + "label": "Scan Barcode", + "options": "Barcode" + }, + { + "allow_bulk_edit": 1, + "fieldname": "items", + "fieldtype": "Table", + "label": "Items", + "oldfieldname": "entries", + "oldfieldtype": "Table", + "options": "Purchase Invoice Item", + "reqd": 1 + }, + { + "fieldname": "pricing_rule_details", + "fieldtype": "Section Break", + "label": "Pricing Rules" + }, + { + "fieldname": "pricing_rules", + "fieldtype": "Table", + "label": "Pricing Rule Detail", + "options": "Pricing Rule Detail", + "read_only": 1 + }, + { + "collapsible_depends_on": "supplied_items", + "fieldname": "raw_materials_supplied", + "fieldtype": "Section Break", + "label": "Raw Materials Supplied" + }, + { + "depends_on": "update_stock", + "fieldname": "supplied_items", + "fieldtype": "Table", + "label": "Supplied Items", + "no_copy": 1, + "options": "Purchase Receipt Item Supplied" + }, + { + "fieldname": "section_break_26", + "fieldtype": "Section Break" + }, + { + "fieldname": "total_qty", + "fieldtype": "Float", + "label": "Total Quantity", + "read_only": 1 + }, + { + "fieldname": "base_total", + "fieldtype": "Currency", + "label": "Total (Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "base_net_total", + "fieldtype": "Currency", + "label": "Net Total (Company Currency)", + "oldfieldname": "net_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, + { + "fieldname": "total", + "fieldtype": "Currency", + "label": "Total", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "net_total", + "fieldtype": "Currency", + "label": "Net Total", + "oldfieldname": "net_total_import", + "oldfieldtype": "Currency", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "total_net_weight", + "fieldtype": "Float", + "label": "Total Net Weight", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "taxes_section", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "fa fa-money" + }, + { + "fieldname": "tax_category", + "fieldtype": "Link", + "label": "Tax Category", + "options": "Tax Category", + "print_hide": 1 + }, + { + "fieldname": "column_break_49", + "fieldtype": "Column Break" + }, + { + "fieldname": "shipping_rule", + "fieldtype": "Link", + "label": "Shipping Rule", + "options": "Shipping Rule", + "print_hide": 1 + }, + { + "fieldname": "section_break_51", + "fieldtype": "Section Break" + }, + { + "fieldname": "taxes_and_charges", + "fieldtype": "Link", + "label": "Purchase Taxes and Charges Template", + "oldfieldname": "purchase_other_charges", + "oldfieldtype": "Link", + "options": "Purchase Taxes and Charges Template", + "print_hide": 1 + }, + { + "fieldname": "taxes", + "fieldtype": "Table", + "label": "Purchase Taxes and Charges", + "oldfieldname": "purchase_tax_details", + "oldfieldtype": "Table", + "options": "Purchase Taxes and Charges" + }, + { + "collapsible": 1, + "fieldname": "sec_tax_breakup", + "fieldtype": "Section Break", + "label": "Tax Breakup" + }, + { + "fieldname": "other_charges_calculation", + "fieldtype": "Long Text", + "label": "Taxes and Charges Calculation", + "no_copy": 1, + "oldfieldtype": "HTML", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "totals", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "fa fa-money" + }, + { + "fieldname": "base_taxes_and_charges_added", + "fieldtype": "Currency", + "label": "Taxes and Charges Added (Company Currency)", + "oldfieldname": "other_charges_added", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "base_taxes_and_charges_deducted", + "fieldtype": "Currency", + "label": "Taxes and Charges Deducted (Company Currency)", + "oldfieldname": "other_charges_deducted", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "base_total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges (Company Currency)", + "oldfieldname": "total_tax", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_40", + "fieldtype": "Column Break" + }, + { + "fieldname": "taxes_and_charges_added", + "fieldtype": "Currency", + "label": "Taxes and Charges Added", + "oldfieldname": "other_charges_added_import", + "oldfieldtype": "Currency", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "taxes_and_charges_deducted", + "fieldtype": "Currency", + "label": "Taxes and Charges Deducted", + "oldfieldname": "other_charges_deducted_import", + "oldfieldtype": "Currency", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "discount_amount", + "fieldname": "section_break_44", + "fieldtype": "Section Break", + "label": "Additional Discount" + }, + { + "default": "Grand Total", + "fieldname": "apply_discount_on", + "fieldtype": "Select", + "label": "Apply Additional Discount On", + "options": "\nGrand Total\nNet Total", + "print_hide": 1 + }, + { + "fieldname": "base_discount_amount", + "fieldtype": "Currency", + "label": "Additional Discount Amount (Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_46", + "fieldtype": "Column Break" + }, + { + "fieldname": "additional_discount_percentage", + "fieldtype": "Float", + "label": "Additional Discount Percentage", + "print_hide": 1 + }, + { + "fieldname": "discount_amount", + "fieldtype": "Currency", + "label": "Additional Discount Amount", + "options": "currency", + "print_hide": 1 + }, + { + "fieldname": "section_break_49", + "fieldtype": "Section Break" + }, + { + "fieldname": "base_grand_total", + "fieldtype": "Currency", + "label": "Grand Total (Company Currency)", + "oldfieldname": "grand_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "eval:!doc.disable_rounded_total", + "fieldname": "base_rounding_adjustment", + "fieldtype": "Currency", + "label": "Rounding Adjustment (Company Currency)", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "eval:!doc.disable_rounded_total", + "fieldname": "base_rounded_total", + "fieldtype": "Currency", + "label": "Rounded Total (Company Currency)", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "base_in_words", + "fieldtype": "Data", + "label": "In Words (Company Currency)", + "length": 240, + "oldfieldname": "in_words", + "oldfieldtype": "Data", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break8", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "print_hide": 1, + "width": "50%" + }, + { + "fieldname": "grand_total", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Grand Total", + "oldfieldname": "grand_total_import", + "oldfieldtype": "Currency", + "options": "currency", + "read_only": 1 + }, + { + "depends_on": "eval:!doc.disable_rounded_total", + "fieldname": "rounding_adjustment", + "fieldtype": "Currency", + "label": "Rounding Adjustment", + "no_copy": 1, + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "eval:!doc.disable_rounded_total", + "fieldname": "rounded_total", + "fieldtype": "Currency", + "label": "Rounded Total", + "no_copy": 1, + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "in_words", + "fieldtype": "Data", + "label": "In Words", + "length": 240, + "oldfieldname": "in_words_import", + "oldfieldtype": "Data", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "total_advance", + "fieldtype": "Currency", + "label": "Total Advance", + "no_copy": 1, + "oldfieldname": "total_advance", + "oldfieldtype": "Currency", + "options": "party_account_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "outstanding_amount", + "fieldtype": "Currency", + "label": "Outstanding Amount", + "no_copy": 1, + "oldfieldname": "outstanding_amount", + "oldfieldtype": "Currency", + "options": "party_account_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "depends_on": "grand_total", + "fieldname": "disable_rounded_total", + "fieldtype": "Check", + "label": "Disable Rounded Total" + }, + { + "collapsible": 1, + "collapsible_depends_on": "paid_amount", + "depends_on": "eval:doc.is_paid===1||(doc.advances && doc.advances.length>0)", + "fieldname": "payments_section", + "fieldtype": "Section Break", + "label": "Payments" + }, + { + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "options": "Mode of Payment", + "print_hide": 1 + }, + { + "fieldname": "cash_bank_account", + "fieldtype": "Link", + "label": "Cash/Bank Account", + "options": "Account" + }, + { + "fieldname": "clearance_date", + "fieldtype": "Date", + "label": "Clearance Date", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "col_br_payments", + "fieldtype": "Column Break" + }, + { + "depends_on": "is_paid", + "fieldname": "paid_amount", + "fieldtype": "Currency", + "label": "Paid Amount", + "no_copy": 1, + "options": "currency", + "print_hide": 1 + }, + { + "fieldname": "base_paid_amount", + "fieldtype": "Currency", + "label": "Paid Amount (Company Currency)", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "write_off_amount", + "depends_on": "grand_total", + "fieldname": "write_off", + "fieldtype": "Section Break", + "label": "Write Off" + }, + { + "fieldname": "write_off_amount", + "fieldtype": "Currency", + "label": "Write Off Amount", + "no_copy": 1, + "options": "currency", + "print_hide": 1 + }, + { + "fieldname": "base_write_off_amount", + "fieldtype": "Currency", + "label": "Write Off Amount (Company Currency)", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_61", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:flt(doc.write_off_amount)!=0", + "fieldname": "write_off_account", + "fieldtype": "Link", + "label": "Write Off Account", + "options": "Account", + "print_hide": 1 + }, + { + "depends_on": "eval:flt(doc.write_off_amount)!=0", + "fieldname": "write_off_cost_center", + "fieldtype": "Link", + "label": "Write Off Cost Center", + "options": "Cost Center", + "print_hide": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "advances", + "fieldname": "advances_section", + "fieldtype": "Section Break", + "label": "Advance Payments", + "oldfieldtype": "Section Break", + "options": "fa fa-money", + "print_hide": 1 + }, + { + "default": "0", + "fieldname": "allocate_advances_automatically", + "fieldtype": "Check", + "label": "Set Advances and Allocate (FIFO)" + }, + { + "depends_on": "eval:!doc.allocate_advances_automatically", + "fieldname": "get_advances", + "fieldtype": "Button", + "label": "Get Advances Paid", + "oldfieldtype": "Button", + "print_hide": 1 + }, + { + "fieldname": "advances", + "fieldtype": "Table", + "label": "Advances", + "no_copy": 1, + "oldfieldname": "advance_allocation_details", + "oldfieldtype": "Table", + "options": "Purchase Invoice Advance", + "print_hide": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:(!doc.is_return)", + "fieldname": "payment_schedule_section", + "fieldtype": "Section Break", + "label": "Payment Terms" + }, + { + "fieldname": "payment_terms_template", + "fieldtype": "Link", + "label": "Payment Terms Template", + "options": "Payment Terms Template" + }, + { + "fieldname": "payment_schedule", + "fieldtype": "Table", + "label": "Payment Schedule", + "no_copy": 1, + "options": "Payment Schedule", + "print_hide": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "terms", + "fieldname": "terms_section_break", + "fieldtype": "Section Break", + "label": "Terms and Conditions", + "options": "fa fa-legal" + }, + { + "fieldname": "tc_name", + "fieldtype": "Link", + "label": "Terms", + "options": "Terms and Conditions", + "print_hide": 1 + }, + { + "fieldname": "terms", + "fieldtype": "Text Editor", + "label": "Terms and Conditions1" + }, + { + "collapsible": 1, + "fieldname": "printing_settings", + "fieldtype": "Section Break", + "label": "Printing Settings" + }, + { + "allow_on_submit": 1, + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "options": "Letter Head", + "print_hide": 1 + }, + { + "allow_on_submit": 1, + "default": "0", + "fieldname": "group_same_items", + "fieldtype": "Check", + "label": "Group same items", + "print_hide": 1 + }, + { + "fieldname": "column_break_112", + "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "fieldname": "select_print_heading", + "fieldtype": "Link", + "label": "Print Heading", + "no_copy": 1, + "oldfieldname": "select_print_heading", + "oldfieldtype": "Link", + "options": "Print Heading", + "print_hide": 1, + "report_hide": 1 + }, + { + "fieldname": "language", + "fieldtype": "Data", + "label": "Print Language", + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "more_info", + "fieldtype": "Section Break", + "label": "More Information", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text", + "print_hide": 1 + }, + { + "default": "0", + "fetch_from": "supplier.is_internal_supplier", + "fieldname": "is_internal_supplier", + "fieldtype": "Check", + "ignore_user_permissions": 1, + "label": "Is Internal Supplier", + "read_only": 1 + }, + { + "fieldname": "credit_to", + "fieldtype": "Link", + "label": "Credit To", + "oldfieldname": "credit_to", + "oldfieldtype": "Link", + "options": "Account", + "print_hide": 1, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "party_account_currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Party Account Currency", + "no_copy": 1, + "options": "Currency", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "No", + "fieldname": "is_opening", + "fieldtype": "Select", + "label": "Is Opening Entry", + "oldfieldname": "is_opening", + "oldfieldtype": "Select", + "options": "No\nYes", + "print_hide": 1 + }, + { + "fieldname": "against_expense_account", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Against Expense Account", + "no_copy": 1, + "oldfieldname": "against_expense_account", + "oldfieldtype": "Small Text", + "print_hide": 1 + }, + { + "fieldname": "column_break_63", + "fieldtype": "Column Break" + }, + { + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nPartly Paid\nUnpaid\nOverdue\nCancelled\nInternal Transfer", + "print_hide": 1 + }, + { + "fieldname": "inter_company_invoice_reference", + "fieldtype": "Link", + "label": "Inter Company Invoice Reference", + "no_copy": 1, + "options": "Sales Invoice", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "remarks", + "fieldtype": "Small Text", + "label": "Remarks", + "no_copy": 1, + "oldfieldname": "remarks", + "oldfieldtype": "Text", + "print_hide": 1 + }, + { + "collapsible": 1, + "fieldname": "subscription_section", + "fieldtype": "Section Break", + "label": "Subscription Section", + "print_hide": 1 + }, + { + "allow_on_submit": 1, + "description": "Start date of current invoice's period", + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "no_copy": 1, + "print_hide": 1 + }, + { + "allow_on_submit": 1, + "description": "End date of current invoice's period", + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "no_copy": 1, + "print_hide": 1 + }, + { + "fieldname": "column_break_114", + "fieldtype": "Column Break" + }, + { + "fieldname": "auto_repeat", + "fieldtype": "Link", + "label": "Auto Repeat", + "no_copy": 1, + "options": "Auto Repeat", + "print_hide": 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" + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions " + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "tax_withholding_category", + "fieldtype": "Link", + "hidden": 1, + "label": "Tax Withholding Category", + "options": "Tax Withholding Category", + "print_hide": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Select Billing Address", + "options": "Address" + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1 + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, + { + "depends_on": "eval:doc.is_internal_supplier", + "description": "Unrealized Profit / Loss account for intra-company transfers", + "fieldname": "unrealized_profit_loss_account", + "fieldtype": "Link", + "label": "Unrealized Profit / Loss Account", + "options": "Account" + }, + { + "depends_on": "eval:doc.is_internal_supplier", + "description": "Company which internal supplier represents", + "fetch_from": "supplier.represents_company", + "fieldname": "represents_company", + "fieldtype": "Link", + "label": "Represents Company", + "options": "Company" + }, + { + "depends_on": "eval:doc.update_stock && doc.is_internal_supplier", + "description": "Sets 'From Warehouse' in each row of the items table.", + "fieldname": "set_from_warehouse", + "fieldtype": "Link", + "label": "Set From Warehouse", + "no_copy": 1, + "options": "Warehouse", + "print_hide": 1, + "print_width": "50px", + "width": "50px" + }, + { + "depends_on": "eval:doc.is_subcontracted", + "fieldname": "supplier_warehouse", + "fieldtype": "Link", + "label": "Supplier Warehouse", + "no_copy": 1, + "options": "Warehouse", + "print_hide": 1, + "print_width": "50px", + "width": "50px" + }, + { + "fieldname": "per_received", + "fieldtype": "Percent", + "hidden": 1, + "label": "Per Received", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "ignore_default_payment_terms_template", + "fieldtype": "Check", + "hidden": 1, + "label": "Ignore Default Payment Terms Template", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "accounting_details_section", + "fieldtype": "Section Break", + "label": "Accounting Details", + "print_hide": 1 + }, + { + "fieldname": "column_break_147", + "fieldtype": "Column Break" + }, + { + "fieldname": "advance_tax", + "fieldtype": "Table", + "hidden": 1, + "label": "Advance Tax", + "options": "Advance Tax", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_old_subcontracting_flow", + "fieldtype": "Check", + "hidden": 1, + "label": "Is Old Subcontracting Flow", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "tax_withholding_net_total", + "fieldtype": "Currency", + "label": "Tax Withholding Net Total", + "no_copy": 1, + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "tax_withheld_vouchers_section", + "fieldtype": "Section Break", + "label": "Tax Withheld Vouchers" + }, + { + "fieldname": "tax_withheld_vouchers", + "fieldtype": "Table", + "label": "Tax Withheld Vouchers", + "options": "Tax Withheld Vouchers", + "read_only": 1 + }, + { + "fieldname": "base_tax_withholding_net_total", + "fieldtype": "Currency", + "label": "Base Tax Withholding Net Total", + "no_copy": 1, + "options": "currency", + "print_hide": 1, + "read_only": 1 + } + ], + "icon": "fa fa-file-text", + "idx": 204, + "is_submittable": 1, + "links": [], + "modified": "2022-09-27 13:52:55.766844", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Purchase Invoice", + "name_case": "Title Case", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase User" + }, + { + "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 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Auditor" + }, + { + "permlevel": 1, + "read": 1, + "role": "Accounts Manager", + "write": 1 + } + ], + "search_fields": "posting_date, supplier, bill_no, base_grand_total, outstanding_amount", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "timeline_field": "supplier", + "title_field": "title", + "track_changes": 1 + } 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 7eddd81ee0..280cc24e2c 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -61,6 +61,9 @@ def get_party_details(inv): def get_party_tax_withholding_details(inv, tax_withholding_category=None): + if inv.doctype == "Payment Entry": + inv.tax_withholding_net_total = inv.net_total + pan_no = "" parties = [] party_type, party = get_party_details(inv) @@ -428,11 +431,11 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers): ): # Get net total again as TDS is calculated on net total # Grand is used to just check for threshold breach - net_total = 0 - if vouchers: - net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)") - - net_total += inv.net_total + net_total = ( + frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)") + or 0.0 + ) + net_total += inv.tax_withholding_net_total supp_credit_amt = net_total - cumulative_threshold if ldc and is_valid_certificate( 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 e80fe11ab3..40c732bae5 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 @@ -186,6 +186,46 @@ class TestTaxWithholdingCategory(unittest.TestCase): for d in reversed(invoices): d.cancel() + def test_tds_calculation_on_net_total_partial_tds(self): + frappe.db.set_value( + "Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS" + ) + invoices = [] + + pi = create_purchase_invoice(supplier="Test TDS Supplier4", rate=20000, do_not_save=True) + pi.extend( + "items", + [ + { + "doctype": "Purchase Invoice Item", + "item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"), + "qty": 1, + "rate": 20000, + "cost_center": "Main - _TC", + "expense_account": "Stock Received But Not Billed - _TC", + "apply_tds": 0, + }, + { + "doctype": "Purchase Invoice Item", + "item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"), + "qty": 1, + "rate": 35000, + "cost_center": "Main - _TC", + "expense_account": "Stock Received But Not Billed - _TC", + "apply_tds": 1, + }, + ], + ) + pi.save() + pi.submit() + invoices.append(pi) + + self.assertEqual(pi.taxes[0].tax_amount, 5500) + + # cancel invoices to avoid clashing + for d in reversed(invoices): + d.cancel() + def test_multi_category_single_supplier(self): frappe.db.set_value( "Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category" diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index cbcccce5f7..ee19adc425 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -64,6 +64,18 @@ class calculate_taxes_and_totals(object): self._cleanup() self.calculate_total_net_weight() + def calculate_tax_withholding_net_total(self): + if hasattr(self.doc, "tax_withholding_net_total"): + sum_net_amount = 0 + sum_base_net_amount = 0 + for item in self.doc.get("items"): + if hasattr(item, "apply_tds") and item.apply_tds: + sum_net_amount += item.net_amount + sum_base_net_amount += item.base_net_amount + + self.doc.tax_withholding_net_total = sum_net_amount + self.doc.base_tax_withholding_net_total = sum_base_net_amount + def validate_item_tax_template(self): for item in self.doc.get("items"): if item.item_code and item.get("item_tax_template"): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index fc63f124e1..fb937f6cd1 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -316,3 +316,4 @@ erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes erpnext.patches.v14_0.fix_subcontracting_receipt_gl_entries erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization +erpnext.patches.v14_0.update_tds_fields \ No newline at end of file diff --git a/erpnext/patches/v14_0/update_tds_fields.py b/erpnext/patches/v14_0/update_tds_fields.py new file mode 100644 index 0000000000..8ecb91bd7f --- /dev/null +++ b/erpnext/patches/v14_0/update_tds_fields.py @@ -0,0 +1,22 @@ +import frappe + +def execute(): + frappe.db.sql(""" + UPDATE + `tabPurchase Invoice Item` + INNER JOIN + `tabPurchase Invoice` + ON + `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.parent + SET + `tabPurchase Invoice Item`.apply_tds = 1 + WHERE + `tabPurchase Invoice`.apply_tds = 1 + and `tabPurchase Invoice`.docstatus = 1 + """) + + frappe.db.sql(""" + UPDATE `tabPurchase Invoice` + SET tax_withholding_net_total = net_total, + base_tax_withholding_net_total = base_net_total + WHERE apply_tds = 1 and docstatus = 1""") \ No newline at end of file From 4a35a224e2885b518d1c7f6700878ea98ac7f4bf Mon Sep 17 00:00:00 2001 From: niralisatapara <88583909+niralisatapara@users.noreply.github.com> Date: Wed, 19 Oct 2022 15:34:41 +0530 Subject: [PATCH 0374/1047] feat: Item Wise TDS Calculation --- erpnext/patches/v14_0/update_tds_fields.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v14_0/update_tds_fields.py b/erpnext/patches/v14_0/update_tds_fields.py index 8ecb91bd7f..a8358c3a5d 100644 --- a/erpnext/patches/v14_0/update_tds_fields.py +++ b/erpnext/patches/v14_0/update_tds_fields.py @@ -19,4 +19,5 @@ def execute(): UPDATE `tabPurchase Invoice` SET tax_withholding_net_total = net_total, base_tax_withholding_net_total = base_net_total - WHERE apply_tds = 1 and docstatus = 1""") \ No newline at end of file + WHERE apply_tds = 1 and docstatus = 1""") + From 430492152f7ec00a7ea530a0aac0e34682f11d10 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 19 Oct 2022 15:37:13 +0530 Subject: [PATCH 0375/1047] fix: Advance paid amount in orders (#32642) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 7f245fd083..94874894b0 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -995,7 +995,9 @@ class PaymentEntry(AccountsController): if self.payment_type in ("Receive", "Pay") and self.party: for d in self.get("references"): if d.allocated_amount and d.reference_doctype in frappe.get_hooks("advance_payment_doctypes"): - frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid() + frappe.get_doc( + d.reference_doctype, d.reference_name, for_update=True + ).set_total_advance_paid() def on_recurring(self, reference_doc, auto_repeat_doc): self.reference_no = reference_doc.name From 23f0bb45b01fbe7f1884bceb7a457969df79034e Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 19 Oct 2022 12:26:56 +0200 Subject: [PATCH 0376/1047] fix: unset contact details --- erpnext/public/js/utils/party.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index 58594b0a13..644adff1e2 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -242,20 +242,29 @@ erpnext.utils.set_taxes = function(frm, triggered_from_field) { }); }; -erpnext.utils.get_contact_details = function(frm) { +erpnext.utils.get_contact_details = function (frm) { if (frm.updating_party_details) return; if (frm.doc["contact_person"]) { frappe.call({ method: "frappe.contacts.doctype.contact.contact.get_contact_details", - args: {contact: frm.doc.contact_person }, - callback: function(r) { - if (r.message) - frm.set_value(r.message); - } - }) + args: { contact: frm.doc.contact_person }, + callback: function (r) { + if (r.message) frm.set_value(r.message); + }, + }); + } else { + frm.set_value({ + contact_person: "", + contact_display: "", + contact_email: "", + contact_mobile: "", + contact_phone: "", + contact_designation: "", + contact_department: "", + }); } -} +}; erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) { if (!value) { From ad278b200769e03b52f0051c26873a9eca5d8b43 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 19 Oct 2022 18:38:34 +0530 Subject: [PATCH 0377/1047] fix: incorrect qty in material request --- .../doctype/production_plan/production_plan.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 4bb4dcc648..000ee07f2c 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -27,6 +27,7 @@ from erpnext.manufacturing.doctype.bom.bom import get_children as get_bom_childr from erpnext.manufacturing.doctype.bom.bom import validate_bom_no from erpnext.manufacturing.doctype.work_order.work_order import get_item_details from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults +from erpnext.stock.get_item_details import get_conversion_factor from erpnext.utilities.transaction_base import validate_uom_is_integer @@ -648,13 +649,23 @@ class ProductionPlan(Document): else: material_request = material_request_map[key] + conversion_factor = 1.0 + if ( + material_request_type == "Purchase" + and item_doc.purchase_uom + and item_doc.purchase_uom != item_doc.stock_uom + ): + conversion_factor = ( + get_conversion_factor(item_doc.name, item_doc.purchase_uom).get("conversion_factor") or 1.0 + ) + # add item material_request.append( "items", { "item_code": item.item_code, "from_warehouse": item.from_warehouse, - "qty": item.quantity, + "qty": item.quantity / conversion_factor, "schedule_date": schedule_date, "warehouse": item.warehouse, "sales_order": item.sales_order, From 4d5ef721f72b068fc2c94c21748373762626f4b3 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 19 Oct 2022 19:08:27 +0530 Subject: [PATCH 0378/1047] test: validate qty and purchase uom in material request which is created from PP --- .../production_plan/test_production_plan.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 60e6398072..c4ab0f886f 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -806,6 +806,35 @@ class TestProductionPlan(FrappeTestCase): self.assertEqual(pln.status, "Completed") self.assertEqual(pln.po_items[0].produced_qty, 5) + def test_material_request_item_for_purchase_uom(self): + from erpnext.stock.doctype.item.test_item import make_item + + fg_item = make_item(properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1"}).name + bom_item = make_item( + properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1", "purchase_uom": "Nos"} + ).name + + if not frappe.db.exists("UOM Conversion Detail", {"parent": bom_item, "uom": "Nos"}): + doc = frappe.get_doc("Item", bom_item) + doc.append("uoms", {"uom": "Nos", "conversion_factor": 10}) + doc.save() + + make_bom(item=fg_item, raw_materials=[bom_item], source_warehouse="_Test Warehouse - _TC") + + pln = create_production_plan( + item_code=fg_item, planned_qty=10, ignore_existing_ordered_qty=1, stock_uom="_Test UOM 1" + ) + + pln.make_material_request() + for row in frappe.get_all( + "Material Request Item", + filters={"production_plan": pln.name}, + fields=["item_code", "uom", "qty"], + ): + self.assertEqual(row.item_code, bom_item) + self.assertEqual(row.uom, "Nos") + self.assertEqual(row.qty, 1) + def create_production_plan(**args): """ From 8f60f0a0cf7f22ee95f353ceba63de004bdc1d47 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 6 Jun 2022 08:50:54 +0530 Subject: [PATCH 0379/1047] feat: Basic Payment Ledger report --- .../report/payment_ledger/__init__.py | 0 .../report/payment_ledger/payment_ledger.js | 59 +++++ .../report/payment_ledger/payment_ledger.json | 32 +++ .../report/payment_ledger/payment_ledger.py | 222 ++++++++++++++++++ 4 files changed, 313 insertions(+) create mode 100644 erpnext/accounts/report/payment_ledger/__init__.py create mode 100644 erpnext/accounts/report/payment_ledger/payment_ledger.js create mode 100644 erpnext/accounts/report/payment_ledger/payment_ledger.json create mode 100644 erpnext/accounts/report/payment_ledger/payment_ledger.py diff --git a/erpnext/accounts/report/payment_ledger/__init__.py b/erpnext/accounts/report/payment_ledger/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/report/payment_ledger/payment_ledger.js b/erpnext/accounts/report/payment_ledger/payment_ledger.js new file mode 100644 index 0000000000..9779844dc9 --- /dev/null +++ b/erpnext/accounts/report/payment_ledger/payment_ledger.js @@ -0,0 +1,59 @@ +// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +function get_filters() { + let filters = [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("Company"), + "reqd": 1 + }, + { + "fieldname":"period_start_date", + "label": __("Start Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1) + }, + { + "fieldname":"period_end_date", + "label": __("End Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.get_today() + }, + { + "fieldname":"account", + "label": __("Account"), + "fieldtype": "MultiSelectList", + "options": "Account", + get_data: function(txt) { + return frappe.db.get_link_options('Account', txt, { + company: frappe.query_report.get_filter_value("company") + }); + } + }, + { + "fieldname":"voucher_no", + "label": __("Voucher No"), + "fieldtype": "Data", + "width": 100, + }, + { + "fieldname":"against_voucher_no", + "label": __("Against Voucher No"), + "fieldtype": "Data", + "width": 100, + }, + + ] + return filters; +} + +frappe.query_reports["Payment Ledger"] = { + "filters": get_filters() +}; diff --git a/erpnext/accounts/report/payment_ledger/payment_ledger.json b/erpnext/accounts/report/payment_ledger/payment_ledger.json new file mode 100644 index 0000000000..716329fbef --- /dev/null +++ b/erpnext/accounts/report/payment_ledger/payment_ledger.json @@ -0,0 +1,32 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2022-06-06 08:50:43.933708", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "modified": "2022-06-06 08:50:43.933708", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Payment Ledger", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Payment Ledger Entry", + "report_name": "Payment Ledger", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + } + ] +} \ No newline at end of file diff --git a/erpnext/accounts/report/payment_ledger/payment_ledger.py b/erpnext/accounts/report/payment_ledger/payment_ledger.py new file mode 100644 index 0000000000..e470c2727e --- /dev/null +++ b/erpnext/accounts/report/payment_ledger/payment_ledger.py @@ -0,0 +1,222 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from collections import OrderedDict + +import frappe +from frappe import _, qb +from frappe.query_builder import Criterion + + +class PaymentLedger(object): + def __init__(self, filters=None): + self.filters = filters + self.columns, self.data = [], [] + self.voucher_dict = OrderedDict() + self.voucher_amount = [] + self.ple = qb.DocType("Payment Ledger Entry") + + def init_voucher_dict(self): + + if self.voucher_amount: + s = set() + # build a set of unique vouchers + for ple in self.voucher_amount: + key = (ple.voucher_type, ple.voucher_no, ple.party) + s.add(key) + + # for each unique vouchers, initialize +/- list + for key in s: + self.voucher_dict[key] = frappe._dict(increase=list(), decrease=list()) + + # for each ple, using against voucher and amount, assign it to +/- list + # group by against voucher + for ple in self.voucher_amount: + against_key = (ple.against_voucher_type, ple.against_voucher_no, ple.party) + target = None + if self.voucher_dict.get(against_key): + if ple.amount > 0: + target = self.voucher_dict.get(against_key).increase + else: + target = self.voucher_dict.get(against_key).decrease + + # this if condition will lose unassigned ple entries(against_voucher doc doesn't have ple) + # need to somehow include the stray entries as well. + if target is not None: + entry = frappe._dict( + company=ple.company, + account=ple.account, + party_type=ple.party_type, + party=ple.party, + voucher_type=ple.voucher_type, + voucher_no=ple.voucher_no, + against_voucher_type=ple.against_voucher_type, + against_voucher_no=ple.against_voucher_no, + amount=ple.amount, + currency=ple.account_currency, + ) + + if self.filters.include_account_currency: + entry["amount_in_account_currency"] = ple.amount_in_account_currency + + target.append(entry) + + def build_data(self): + self.data.clear() + + for value in self.voucher_dict.values(): + voucher_data = [] + if value.increase != []: + voucher_data.extend(value.increase) + if value.decrease != []: + voucher_data.extend(value.decrease) + + if voucher_data: + # balance row + total = 0 + total_in_account_currency = 0 + + for x in voucher_data: + total += x.amount + if self.filters.include_account_currency: + total_in_account_currency += x.amount_in_account_currency + + entry = frappe._dict( + against_voucher_no="Outstanding:", + amount=total, + currency=voucher_data[0].currency, + ) + + if self.filters.include_account_currency: + entry["amount_in_account_currency"] = total_in_account_currency + + voucher_data.append(entry) + + # empty row + voucher_data.append(frappe._dict()) + self.data.extend(voucher_data) + + def build_conditions(self): + self.conditions = [] + + if self.filters.company: + self.conditions.append(self.ple.company == self.filters.company) + + if self.filters.account: + self.conditions.append(self.ple.account.isin(self.filters.account)) + + if self.filters.period_start_date: + self.conditions.append(self.ple.posting_date.gte(self.filters.period_start_date)) + + if self.filters.period_end_date: + self.conditions.append(self.ple.posting_date.lte(self.filters.period_end_date)) + + if self.filters.voucher_no: + self.conditions.append(self.ple.voucher_no == self.filters.voucher_no) + + if self.filters.against_voucher_no: + self.conditions.append(self.ple.against_voucher_no == self.filters.against_voucher_no) + + def get_data(self): + ple = self.ple + + self.build_conditions() + + # fetch data from table + self.voucher_amount = ( + qb.from_(ple) + .select(ple.star) + .where(ple.delinked == 0) + .where(Criterion.all(self.conditions)) + .run(as_dict=True) + ) + + def get_columns(self): + options = None + self.columns.append( + dict(label=_("Company"), fieldname="company", fieldtype="data", options=options, width="100") + ) + + self.columns.append( + dict(label=_("Account"), fieldname="account", fieldtype="data", options=options, width="100") + ) + + self.columns.append( + dict( + label=_("Party Type"), fieldname="party_type", fieldtype="data", options=options, width="100" + ) + ) + self.columns.append( + dict(label=_("Party"), fieldname="party", fieldtype="data", options=options, width="100") + ) + self.columns.append( + dict( + label=_("Voucher Type"), + fieldname="voucher_type", + fieldtype="data", + options=options, + width="100", + ) + ) + self.columns.append( + dict( + label=_("Voucher No"), fieldname="voucher_no", fieldtype="data", options=options, width="100" + ) + ) + self.columns.append( + dict( + label=_("Against Voucher Type"), + fieldname="against_voucher_type", + fieldtype="data", + options=options, + width="100", + ) + ) + self.columns.append( + dict( + label=_("Against Voucher No"), + fieldname="against_voucher_no", + fieldtype="data", + options=options, + width="100", + ) + ) + self.columns.append( + dict( + label=_("Amount"), + fieldname="amount", + fieldtype="Currency", + options="Company:company:default_currency", + width="100", + ) + ) + + if self.filters.include_account_currency: + self.columns.append( + dict( + label=_("Amount in Account Currency"), + fieldname="amount_in_account_currency", + fieldtype="Currency", + options="currency", + width="100", + ) + ) + self.columns.append( + dict(label=_("Currency"), fieldname="currency", fieldtype="Currency", hidden=True) + ) + + def run(self): + self.get_columns() + self.get_data() + + # initialize dictionary and group using against voucher + self.init_voucher_dict() + + # convert dictionary to list and add balance rows + self.build_data() + + return self.columns, self.data + + +def execute(filters=None): + return PaymentLedger(filters).run() From 6e55b419a6b4130b346a9c105d1d7e887f3082bf Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 19 Oct 2022 13:36:34 +0530 Subject: [PATCH 0380/1047] test: invoice outstandings and payments --- .../payment_ledger/test_payment_ledger.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 erpnext/accounts/report/payment_ledger/test_payment_ledger.py diff --git a/erpnext/accounts/report/payment_ledger/test_payment_ledger.py b/erpnext/accounts/report/payment_ledger/test_payment_ledger.py new file mode 100644 index 0000000000..5ae9b87cde --- /dev/null +++ b/erpnext/accounts/report/payment_ledger/test_payment_ledger.py @@ -0,0 +1,65 @@ +import unittest + +import frappe +from frappe import qb +from frappe.tests.utils import FrappeTestCase + +from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.accounts.report.payment_ledger.payment_ledger import execute + + +class TestPaymentLedger(FrappeTestCase): + def setUp(self): + self.create_company() + self.cleanup() + + def cleanup(self): + doctypes = [] + doctypes.append(qb.DocType("GL Entry")) + doctypes.append(qb.DocType("Payment Ledger Entry")) + doctypes.append(qb.DocType("Sales Invoice")) + doctypes.append(qb.DocType("Payment Entry")) + + for doctype in doctypes: + qb.from_(doctype).delete().where(doctype.company == self.company).run() + + def create_company(self): + name = "Test Payment Ledger" + company = None + if frappe.db.exists("Company", name): + company = frappe.get_doc("Company", name) + else: + company = frappe.get_doc( + { + "doctype": "Company", + "company_name": name, + "country": "India", + "default_currency": "INR", + "create_chart_of_accounts_based_on": "Standard Template", + "chart_of_accounts": "Standard", + } + ) + company = company.save() + self.company = company.name + self.cost_center = company.cost_center + self.warehouse = "All Warehouses" + " - " + company.abbr + self.income_account = company.default_income_account + self.expense_account = company.default_expense_account + self.debit_to = company.default_receivable_account + + def test_unpaid_invoice_outstanding(self): + sinv = create_sales_invoice( + company=self.company, + debit_to=self.debit_to, + expense_account=self.expense_account, + cost_center=self.cost_center, + income_account=self.income_account, + warehouse=self.warehouse, + ) + pe = get_payment_entry(sinv.doctype, sinv.name).save().submit() + + filters = frappe._dict({"company": self.company}) + columns, data = execute(filters=filters) + outstanding = [x for x in data if x.get("against_voucher_no") == "Outstanding:"] + self.assertEqual(outstanding[0].get("amount"), 0) From 4ad3002861339651e85ac9ab4e532251b03590b2 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Wed, 19 Oct 2022 18:37:02 +0200 Subject: [PATCH 0381/1047] fix: allow to create Sales Order from expired Quotation (#32641) --- erpnext/selling/doctype/quotation/quotation.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 70ae085051..6b42e4daea 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -84,11 +84,12 @@ erpnext.selling.QuotationController = class QuotationController extends erpnext. } } - if(doc.docstatus == 1 && !(['Lost', 'Ordered']).includes(doc.status)) { - if(!doc.valid_till || frappe.datetime.get_diff(doc.valid_till, frappe.datetime.get_today()) >= 0) { - cur_frm.add_custom_button(__('Sales Order'), - cur_frm.cscript['Make Sales Order'], __('Create')); - } + if (doc.docstatus == 1 && !["Lost", "Ordered"].includes(doc.status)) { + this.frm.add_custom_button( + __("Sales Order"), + this.frm.cscript["Make Sales Order"], + __("Create") + ); if(doc.status!=="Ordered") { this.frm.add_custom_button(__('Set as Lost'), () => { From c52b41d311b67887834c78072760b3780b4fb1c3 Mon Sep 17 00:00:00 2001 From: "FinByz Tech Pvt. Ltd" Date: Wed, 19 Oct 2022 23:14:10 +0530 Subject: [PATCH 0382/1047] feat(report): added account wise redirection * feat(report):added account wise redirection Co-authored-by: Deepesh Garg --- erpnext/public/js/financial_statements.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 1a309ba015..b0082bdb28 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -28,7 +28,7 @@ erpnext.financial_statements = { }, "open_general_ledger": function(data) { if (!data.account) return; - var project = $.grep(frappe.query_report.filters, function(e){ return e.df.fieldname == 'project'; }) + let project = $.grep(frappe.query_report.filters, function(e){ return e.df.fieldname == 'project'; }); frappe.route_options = { "account": data.account, @@ -37,7 +37,16 @@ erpnext.financial_statements = { "to_date": data.to_date || data.year_end_date, "project": (project && project.length > 0) ? project[0].$input.val() : "" }; - frappe.set_route("query-report", "General Ledger"); + + let report = "General Ledger"; + + if (["Payable", "Receivable"].includes(data.account_type)) { + report = data.account_type == "Payable" ? "Accounts Payable" : "Accounts Receivable"; + frappe.route_options["party_account"] = data.account; + frappe.route_options["report_date"] = data.year_end_date; + } + + frappe.set_route("query-report", report); }, "tree": true, "name_field": "account", From 796f2d3c09ae082c20ad1a2501b55db58477a085 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 19 Oct 2022 23:50:39 +0530 Subject: [PATCH 0383/1047] fix: Billing Address for inter-company purchase docs --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 3 +++ erpnext/stock/doctype/delivery_note/delivery_note.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index afd5a59df4..0c03c550ba 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -2017,6 +2017,9 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): update_address( target_doc, "shipping_address", "shipping_address_display", source_doc.customer_address ) + update_address( + target_doc, "billing_address", "billing_address_display", source_doc.customer_address + ) if currency: target_doc.currency = currency diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 36d5a6ce0e..9dd28dc60b 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -842,6 +842,9 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): update_address( target_doc, "shipping_address", "shipping_address_display", source_doc.customer_address ) + update_address( + target_doc, "billing_address", "billing_address_display", source_doc.customer_address + ) update_taxes( target_doc, From 1ca472cc8a795fed132979a9ba3a2538ab13ca0a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 20 Oct 2022 12:09:42 +0530 Subject: [PATCH 0384/1047] chore: Linting issues --- erpnext/accounts/doctype/subscription_plan/subscription_plan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py index dcd40b11df..f3acdc5aa8 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py @@ -3,12 +3,12 @@ import frappe +from dateutil import relativedelta from frappe import _ from frappe.model.document import Document from frappe.utils import date_diff, flt, get_first_day, get_last_day, getdate from erpnext.utilities.product import get_price -from dateutil import relativedelta class SubscriptionPlan(Document): From 9cfe527492cb740d40d290b6f8ef5da9dfbaed76 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 20 Oct 2022 13:49:46 +0530 Subject: [PATCH 0385/1047] fix: BOM cost update message --- erpnext/manufacturing/doctype/bom/bom.py | 12 ++++++++- erpnext/manufacturing/doctype/bom/test_bom.py | 27 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index ff84991c36..580838e215 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -385,6 +385,7 @@ class BOM(WebsiteGenerator): if self.docstatus == 2: return + self.flags.cost_updated = False existing_bom_cost = self.total_cost if self.docstatus == 1: @@ -407,7 +408,11 @@ class BOM(WebsiteGenerator): frappe.get_doc("BOM", bom).update_cost(from_child_bom=True) if not from_child_bom: - frappe.msgprint(_("Cost Updated"), alert=True) + msg = "Cost Updated" + if not self.flags.cost_updated: + msg = "No changes in cost found" + + frappe.msgprint(_(msg), alert=True) def update_parent_cost(self): if self.total_cost: @@ -593,11 +598,16 @@ class BOM(WebsiteGenerator): # not via doc event, table is not regenerated and needs updation self.calculate_exploded_cost() + old_cost = self.total_cost + self.total_cost = self.operating_cost + self.raw_material_cost - self.scrap_material_cost self.base_total_cost = ( self.base_operating_cost + self.base_raw_material_cost - self.base_scrap_material_cost ) + if self.total_cost != old_cost: + self.flags.cost_updated = True + def calculate_op_cost(self, update_hour_rate=False): """Update workstation rate and calculates totals""" self.operating_cost = 0 diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index 27f3cc905b..e34ac12cd2 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -9,7 +9,10 @@ import frappe from frappe.tests.utils import FrappeTestCase from frappe.utils import cstr, flt -from erpnext.controllers.tests.test_subcontracting_controller import set_backflush_based_on +from erpnext.controllers.tests.test_subcontracting_controller import ( + make_stock_in_entry, + set_backflush_based_on, +) from erpnext.manufacturing.doctype.bom.bom import BOMRecursionError, item_query, make_variant_bom from erpnext.manufacturing.doctype.bom_update_log.test_bom_update_log import ( update_cost_in_all_boms_in_test, @@ -639,6 +642,28 @@ class TestBOM(FrappeTestCase): bom.submit() self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate) + def test_bom_cost_update_flag(self): + rm_item = make_item( + properties={"is_stock_item": 1, "valuation_rate": 99, "last_purchase_rate": 89} + ).name + fg_item = make_item(properties={"is_stock_item": 1}).name + + from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom + + bom = make_bom(item=fg_item, raw_materials=[rm_item]) + + create_stock_reconciliation( + item_code=rm_item, warehouse="_Test Warehouse - _TC", qty=100, rate=600 + ) + + bom.load_from_db() + bom.update_cost() + self.assertTrue(bom.flags.cost_updated) + + bom.load_from_db() + bom.update_cost() + self.assertFalse(bom.flags.cost_updated) + def get_default_bom(item_code="_Test FG Item 2"): return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1}) From b88e850d55f6ced279990de5ff4995ef61b0ea0b Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Thu, 20 Oct 2022 06:26:07 -0400 Subject: [PATCH 0386/1047] perf: cache barcode scan result (#32629) * perf: cache barcode scan result * feat: BarcodeScanResult type * fix: use safe `get_value` `set_value` Co-authored-by: Ankush Menat --- erpnext/stock/utils.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 9fb3be5188..c8ca8a8f65 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -13,6 +13,8 @@ from frappe.utils import cstr, flt, get_link_to_form, nowdate, nowtime import erpnext from erpnext.stock.valuation import FIFOValuation, LIFOValuation +BarcodeScanResult = Dict[str, Optional[str]] + class InvalidWarehouseCompany(frappe.ValidationError): pass @@ -552,7 +554,16 @@ def check_pending_reposting(posting_date: str, throw_error: bool = True) -> bool @frappe.whitelist() -def scan_barcode(search_value: str) -> Dict[str, Optional[str]]: +def scan_barcode(search_value: str) -> BarcodeScanResult: + def set_cache(data: BarcodeScanResult): + frappe.cache().set_value(f"erpnext:barcode_scan:{search_value}", data, expires_in_sec=120) + + def get_cache() -> Optional[BarcodeScanResult]: + if data := frappe.cache().get_value(f"erpnext:barcode_scan:{search_value}"): + return data + + if scan_data := get_cache(): + return scan_data # search barcode no barcode_data = frappe.db.get_value( @@ -562,7 +573,9 @@ def scan_barcode(search_value: str) -> Dict[str, Optional[str]]: as_dict=True, ) if barcode_data: - return _update_item_info(barcode_data) + _update_item_info(barcode_data) + set_cache(barcode_data) + return barcode_data # search serial no serial_no_data = frappe.db.get_value( @@ -572,7 +585,9 @@ def scan_barcode(search_value: str) -> Dict[str, Optional[str]]: as_dict=True, ) if serial_no_data: - return _update_item_info(serial_no_data) + _update_item_info(serial_no_data) + set_cache(serial_no_data) + return serial_no_data # search batch no batch_no_data = frappe.db.get_value( @@ -582,6 +597,8 @@ def scan_barcode(search_value: str) -> Dict[str, Optional[str]]: as_dict=True, ) if batch_no_data: + _update_item_info(batch_no_data) + set_cache(batch_no_data) return _update_item_info(batch_no_data) return {} From 11207c4e562c565046e26e103c7af0e029ff5ea2 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 20 Oct 2022 16:17:57 +0530 Subject: [PATCH 0387/1047] fix: dont update item info twice [skip ci] --- erpnext/stock/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index c8ca8a8f65..b8c5187b2c 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -599,7 +599,7 @@ def scan_barcode(search_value: str) -> BarcodeScanResult: if batch_no_data: _update_item_info(batch_no_data) set_cache(batch_no_data) - return _update_item_info(batch_no_data) + return batch_no_data return {} From feaa2dbba8226adea4094d87fd783877364c1335 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 14 Oct 2022 14:04:43 +0530 Subject: [PATCH 0388/1047] refactor: rewrite `Stock Ledger Report` queries in `QB` --- .../stock/report/stock_ledger/stock_ledger.py | 84 +++++++++++-------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index e18d4c7522..b836e9c242 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -318,20 +318,25 @@ def get_inventory_dimension_fields(): def get_items(filters): + item = frappe.qb.DocType("Item") + query = frappe.qb.from_(item).select(item.name) conditions = [] - if filters.get("item_code"): - conditions.append("item.name=%(item_code)s") + + if item_code := filters.get("item_code"): + conditions.append(item.name == item_code) else: - if filters.get("brand"): - conditions.append("item.brand=%(brand)s") - if filters.get("item_group"): - conditions.append(get_item_group_condition(filters.get("item_group"))) + if brand := filters.get("brand"): + conditions.append(item.brand == brand) + if item_group := filters.get("item_group"): + if condition := get_item_group_condition(item_group, item): + conditions.append(condition) items = [] if conditions: - items = frappe.db.sql_list( - """select name from `tabItem` item where {}""".format(" and ".join(conditions)), filters - ) + for condition in conditions: + query = query.where(condition) + items = [r[0] for r in query.run()] + return items @@ -343,29 +348,22 @@ def get_item_details(items, sl_entries, include_uom): if not items: return item_details - cf_field = cf_join = "" + item = frappe.qb.DocType("Item") + query = ( + frappe.qb.from_(item) + .select(item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom) + .where(item.name.isin(items)) + ) + if include_uom: - cf_field = ", ucd.conversion_factor" - cf_join = ( - "left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom=%s" - % frappe.db.escape(include_uom) + ucd = frappe.qb.DocType("UOM Conversion Detail") + query = ( + query.left_join(ucd) + .on((ucd.parent == item.name) & (ucd.uom == include_uom)) + .select(ucd.conversion_factor) ) - res = frappe.db.sql( - """ - select - item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom {cf_field} - from - `tabItem` item - {cf_join} - where - item.name in ({item_codes}) - """.format( - cf_field=cf_field, cf_join=cf_join, item_codes=",".join(["%s"] * len(items)) - ), - items, - as_dict=1, - ) + res = query.run(as_dict=True) for item in res: item_details.setdefault(item.name, item) @@ -440,16 +438,28 @@ def get_warehouse_condition(warehouse): return "" -def get_item_group_condition(item_group): +def get_item_group_condition(item_group, item_table=None): item_group_details = frappe.db.get_value("Item Group", item_group, ["lft", "rgt"], as_dict=1) if item_group_details: - return ( - "item.item_group in (select ig.name from `tabItem Group` ig \ - where ig.lft >= %s and ig.rgt <= %s and item.item_group = ig.name)" - % (item_group_details.lft, item_group_details.rgt) - ) - - return "" + if item_table: + ig = frappe.qb.DocType("Item Group") + return item_table.item_group.isin( + ( + frappe.qb.from_(ig) + .select(ig.name) + .where( + (ig.lft >= item_group_details.lft) + & (ig.rgt <= item_group_details.rgt) + & (item_table.item_group == ig.name) + ) + ) + ) + else: + return ( + "item.item_group in (select ig.name from `tabItem Group` ig \ + where ig.lft >= %s and ig.rgt <= %s and item.item_group = ig.name)" + % (item_group_details.lft, item_group_details.rgt) + ) def check_inventory_dimension_filters_applied(filters) -> bool: From cde785f1bb8692fbb6f22f554f56f5f463488ab9 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 14 Oct 2022 15:35:19 +0530 Subject: [PATCH 0389/1047] refactor: rewrite `Product Bundle Balance Report` queries in `QB` --- .../product_bundle_balance.py | 158 +++++++++--------- 1 file changed, 83 insertions(+), 75 deletions(-) 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 854875a053..9e75201bd1 100644 --- a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py +++ b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py @@ -4,7 +4,9 @@ import frappe from frappe import _ +from frappe.query_builder.functions import IfNull from frappe.utils import flt +from pypika.terms import ExistsCriterion from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition @@ -123,43 +125,65 @@ def get_items(filters): pb_details = frappe._dict() item_details = frappe._dict() - conditions = get_parent_item_conditions(filters) - parent_item_details = frappe.db.sql( - """ - select item.name as item_code, item.item_name, pb.description, item.item_group, item.brand, item.stock_uom - from `tabItem` item - inner join `tabProduct Bundle` pb on pb.new_item_code = item.name - where ifnull(item.disabled, 0) = 0 {0} - """.format( - conditions - ), - filters, - as_dict=1, - ) # nosec + item = frappe.qb.DocType("Item") + pb = frappe.qb.DocType("Product Bundle") + + query = ( + frappe.qb.from_(item) + .inner_join(pb) + .on(pb.new_item_code == item.name) + .select( + item.name.as_("item_code"), + item.item_name, + pb.description, + item.item_group, + item.brand, + item.stock_uom, + ) + .where(IfNull(item.disabled, 0) == 0) + ) + + if item_code := filters.get("item_code"): + query = query.where(item.item_code == item_code) + else: + if brand := filters.get("brand"): + query = query.where(item.brand == brand) + if item_group := filters.get("item_group"): + if conditions := get_item_group_condition(item_group, item): + query = query.where(conditions) + + parent_item_details = query.run(as_dict=True) parent_items = [] for d in parent_item_details: parent_items.append(d.item_code) item_details[d.item_code] = d + child_item_details = [] if parent_items: - child_item_details = frappe.db.sql( - """ - select - pb.new_item_code as parent_item, pbi.item_code, item.item_name, pbi.description, item.item_group, item.brand, - item.stock_uom, pbi.uom, pbi.qty - from `tabProduct Bundle Item` pbi - inner join `tabProduct Bundle` pb on pb.name = pbi.parent - inner join `tabItem` item on item.name = pbi.item_code - where pb.new_item_code in ({0}) - """.format( - ", ".join(["%s"] * len(parent_items)) - ), - parent_items, - as_dict=1, - ) # nosec - else: - child_item_details = [] + item = frappe.qb.DocType("Item") + pb = frappe.qb.DocType("Product Bundle") + pbi = frappe.qb.DocType("Product Bundle Item") + + child_item_details = ( + frappe.qb.from_(pbi) + .inner_join(pb) + .on(pb.name == pbi.parent) + .inner_join(item) + .on(item.name == pbi.item_code) + .select( + pb.new_item_code.as_("parent_item"), + pbi.item_code, + item.item_name, + pbi.description, + item.item_group, + item.brand, + item.stock_uom, + pbi.uom, + pbi.qty, + ) + .where(pb.new_item_code.isin(parent_items)) + ).run(as_dict=1) child_items = set() for d in child_item_details: @@ -184,58 +208,42 @@ def get_stock_ledger_entries(filters, items): if not items: return [] - item_conditions_sql = " and sle.item_code in ({})".format( - ", ".join(frappe.db.escape(i) for i in items) + sle = frappe.qb.DocType("Stock Ledger Entry") + sle2 = frappe.qb.DocType("Stock Ledger Entry") + + query = ( + frappe.qb.from_(sle) + .force_index("posting_sort_index") + .left_join(sle2) + .on( + (sle.item_code == sle2.item_code) + & (sle.warehouse == sle2.warehouse) + & (sle.posting_date < sle2.posting_date) + & (sle.posting_time < sle2.posting_time) + & (sle.name < sle2.name) + ) + .select(sle.item_code, sle.warehouse, sle.qty_after_transaction, sle.company) + .where((sle2.name.isnull()) & (sle.docstatus < 2) & (sle.item_code.isin(items))) ) - conditions = get_sle_conditions(filters) - - return frappe.db.sql( - """ - select - sle.item_code, sle.warehouse, sle.qty_after_transaction, sle.company - from - `tabStock Ledger Entry` sle force index (posting_sort_index) - left join `tabStock Ledger Entry` sle2 on - sle.item_code = sle2.item_code and sle.warehouse = sle2.warehouse - and (sle.posting_date, sle.posting_time, sle.name) < (sle2.posting_date, sle2.posting_time, sle2.name) - where sle2.name is null and sle.docstatus < 2 %s %s""" - % (item_conditions_sql, conditions), - as_dict=1, - ) # nosec - - -def get_parent_item_conditions(filters): - conditions = [] - - if filters.get("item_code"): - conditions.append("item.item_code = %(item_code)s") + if date := filters.get("date"): + query = query.where(sle.posting_date <= date) else: - if filters.get("brand"): - conditions.append("item.brand=%(brand)s") - if filters.get("item_group"): - conditions.append(get_item_group_condition(filters.get("item_group"))) - - conditions = " and ".join(conditions) - return "and {0}".format(conditions) if conditions else "" - - -def get_sle_conditions(filters): - conditions = "" - if not filters.get("date"): frappe.throw(_("'Date' is required")) - conditions += " and sle.posting_date <= %s" % frappe.db.escape(filters.get("date")) - if filters.get("warehouse"): warehouse_details = frappe.db.get_value( "Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1 ) - if warehouse_details: - conditions += ( - " and exists (select name from `tabWarehouse` wh \ - where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)" - % (warehouse_details.lft, warehouse_details.rgt) - ) # nosec - return conditions + if warehouse_details: + wh = frappe.qb.DocType("Warehouse") + query = query.where( + ExistsCriterion( + frappe.qb.from_(wh) + .select(wh.name) + .where((wh.lft >= warehouse_details.lft) & (wh.rgt <= warehouse_details.rgt)) + ) + ) + + return query.run(as_dict=True) From 40bd1215932689cc37619a2797298e55ed2235cc Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 14 Oct 2022 16:33:02 +0530 Subject: [PATCH 0390/1047] refactor: rewrite `Itemwise Recommended Reorder Level Report` queries in `QB` --- .../itemwise_recommended_reorder_level.py | 130 +++++++++--------- 1 file changed, 68 insertions(+), 62 deletions(-) diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py index f308e9e41f..a6fc049cbd 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 @@ -3,6 +3,7 @@ import frappe from frappe import _ +from frappe.query_builder.functions import Abs, Sum from frappe.utils import flt, getdate @@ -11,8 +12,6 @@ def execute(filters=None): filters = {} float_precision = frappe.db.get_default("float_precision") - condition = get_condition(filters) - avg_daily_outgoing = 0 diff = ((getdate(filters.get("to_date")) - getdate(filters.get("from_date"))).days) + 1 if diff <= 0: @@ -20,8 +19,8 @@ def execute(filters=None): columns = get_columns() items = get_item_info(filters) - consumed_item_map = get_consumed_items(condition) - delivered_item_map = get_delivered_items(condition) + consumed_item_map = get_consumed_items(filters) + delivered_item_map = get_delivered_items(filters) data = [] for item in items: @@ -71,76 +70,86 @@ def get_columns(): def get_item_info(filters): from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition - conditions = [get_item_group_condition(filters.get("item_group"))] - if filters.get("brand"): - conditions.append("item.brand=%(brand)s") - conditions.append("is_stock_item = 1") - - return frappe.db.sql( - """select name, item_name, description, brand, item_group, - safety_stock, lead_time_days from `tabItem` item where {}""".format( - " and ".join(conditions) - ), - filters, - as_dict=1, + item = frappe.qb.DocType("Item") + query = ( + frappe.qb.from_(item) + .select( + item.name, + item.item_name, + item.description, + item.brand, + item.item_group, + item.safety_stock, + item.lead_time_days, + ) + .where(item.is_stock_item == 1) ) + if brand := filters.get("brand"): + query = query.where(item.brand == brand) -def get_consumed_items(condition): + if conditions := get_item_group_condition(filters.get("item_group"), item): + query = query.where(conditions) + + return query.run(as_dict=True) + + +def get_consumed_items(filters): purpose_to_exclude = [ "Material Transfer for Manufacture", "Material Transfer", "Send to Subcontractor", ] - condition += """ - and ( - purpose is NULL - or purpose not in ({}) + se = frappe.qb.DocType("Stock Entry") + sle = frappe.qb.DocType("Stock Ledger Entry") + query = ( + frappe.qb.from_(sle) + .left_join(se) + .on(sle.voucher_no == se.name) + .select(sle.item_code, Abs(Sum(sle.actual_qty)).as_("consumed_qty")) + .where( + (sle.actual_qty < 0) + & (sle.is_cancelled == 0) + & (sle.voucher_type.notin(["Delivery Note", "Sales Invoice"])) + & ((se.purpose.isnull()) | (se.purpose.notin(purpose_to_exclude))) ) - """.format( - ", ".join(f"'{p}'" for p in purpose_to_exclude) + .groupby(sle.item_code) ) - condition = condition.replace("posting_date", "sle.posting_date") + query = get_filtered_query(filters, sle, query) - consumed_items = frappe.db.sql( - """ - select item_code, abs(sum(actual_qty)) as consumed_qty - from `tabStock Ledger Entry` as sle left join `tabStock Entry` as se - on sle.voucher_no = se.name - where - actual_qty < 0 - and is_cancelled = 0 - and voucher_type not in ('Delivery Note', 'Sales Invoice') - %s - group by item_code""" - % condition, - as_dict=1, - ) + consumed_items = query.run(as_dict=True) consumed_items_map = {item.item_code: item.consumed_qty for item in consumed_items} return consumed_items_map -def get_delivered_items(condition): - dn_items = frappe.db.sql( - """select dn_item.item_code, sum(dn_item.stock_qty) as dn_qty - from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item - where dn.name = dn_item.parent and dn.docstatus = 1 %s - group by dn_item.item_code""" - % (condition), - as_dict=1, +def get_delivered_items(filters): + parent = frappe.qb.DocType("Delivery Note") + child = frappe.qb.DocType("Delivery Note Item") + query = ( + frappe.qb.from_(parent) + .from_(child) + .select(child.item_code, Sum(child.stock_qty).as_("dn_qty")) + .where((parent.name == child.parent) & (parent.docstatus == 1)) + .groupby(child.item_code) ) + query = get_filtered_query(filters, parent, query) - si_items = frappe.db.sql( - """select si_item.item_code, sum(si_item.stock_qty) as si_qty - from `tabSales Invoice` si, `tabSales Invoice Item` si_item - where si.name = si_item.parent and si.docstatus = 1 and - si.update_stock = 1 %s - group by si_item.item_code""" - % (condition), - as_dict=1, + dn_items = query.run(as_dict=True) + + parent = frappe.qb.DocType("Sales Invoice") + child = frappe.qb.DocType("Sales Invoice Item") + query = ( + frappe.qb.from_(parent) + .from_(child) + .select(child.item_code, Sum(child.stock_qty).as_("si_qty")) + .where((parent.name == child.parent) & (parent.docstatus == 1) & (parent.update_stock == 1)) + .groupby(child.item_code) ) + query = get_filtered_query(filters, parent, query) + + si_items = query.run(as_dict=True) dn_item_map = {} for item in dn_items: @@ -152,13 +161,10 @@ def get_delivered_items(condition): return dn_item_map -def get_condition(filters): - conditions = "" +def get_filtered_query(filters, table, query): if filters.get("from_date") and filters.get("to_date"): - conditions += " and posting_date between '%s' and '%s'" % ( - filters["from_date"], - filters["to_date"], - ) + query = query.where(table.posting_date.between(filters["from_date"], filters["to_date"])) else: - frappe.throw(_("From and To dates required")) - return conditions + frappe.throw(_("From and To dates are required")) + + return query From ef0cb17fafce696fff11685a5cf74e936a3f0508 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 22 Oct 2022 23:46:01 +0530 Subject: [PATCH 0391/1047] chore: Add repayment date on option --- erpnext/loan_management/doctype/loan/loan.py | 34 ++++-- .../loan_management/doctype/loan/test_loan.py | 104 +++++++++++++++++- .../doctype/loan_type/loan_type.json | 13 ++- 3 files changed, 138 insertions(+), 13 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index d55af703c0..0c9c97f60f 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -12,7 +12,6 @@ from frappe.utils import ( add_months, date_diff, flt, - get_first_day, get_last_day, getdate, now_datetime, @@ -117,36 +116,51 @@ class Loan(AccountsController): if not self.repayment_start_date: frappe.throw(_("Repayment Start Date is mandatory for term loans")) - schedule_type = frappe.db.get_value("Loan Type", self.loan_type, "repayment_schedule_type") + schedule_type_details = frappe.db.get_value( + "Loan Type", self.loan_type, ["repayment_schedule_type", "repayment_date_on"], as_dict=1 + ) + self.repayment_schedule = [] payment_date = self.repayment_start_date balance_amount = self.loan_amount while balance_amount > 0: interest_amount, principal_amount, balance_amount, total_payment = self.get_amounts( - payment_date, balance_amount, schedule_type + payment_date, + balance_amount, + schedule_type_details.repayment_schedule_type, + schedule_type_details.repayment_date_on, ) - if schedule_type == "Pro-rated calendar months": - next_payment_date = add_days(get_last_day(payment_date), 1) + if schedule_type_details.repayment_schedule_type == "Pro-rated calendar months": + next_payment_date = get_last_day(payment_date) + if schedule_type_details.repayment_date_on == "Start of the next month": + next_payment_date = add_days(next_payment_date, 1) + payment_date = next_payment_date self.add_repayment_schedule_row( payment_date, principal_amount, interest_amount, total_payment, balance_amount ) - if schedule_type == "Monthly as per repayment start date": + if ( + schedule_type_details.repayment_schedule_type == "Monthly as per repayment start date" + or schedule_type_details.repayment_date_on == "End of the current month" + ): next_payment_date = add_single_month(payment_date) payment_date = next_payment_date - def get_amounts(self, payment_date, balance_amount, schedule_type): - first_day_of_month = get_first_day(payment_date) - + def get_amounts(self, payment_date, balance_amount, schedule_type, repayment_date_on): if schedule_type == "Monthly as per repayment start date": days = 1 months = 12 else: - if first_day_of_month == payment_date: + expected_payment_date = get_last_day(payment_date) + if repayment_date_on == "Start of the next month": + expected_payment_date = add_days(expected_payment_date, 1) + + if expected_payment_date == payment_date: + # using 30 days for calculating interest for all full months days = 30 months = 365 else: diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index 2608e42e08..0e61161601 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -4,7 +4,16 @@ import unittest import frappe -from frappe.utils import add_days, add_months, add_to_date, date_diff, flt, get_datetime, nowdate +from frappe.utils import ( + add_days, + add_months, + add_to_date, + date_diff, + flt, + format_date, + get_datetime, + nowdate, +) from erpnext.loan_management.doctype.loan.loan import ( make_loan_write_off, @@ -49,6 +58,50 @@ class TestLoan(unittest.TestCase): penalty_income_account="Penalty Income Account - _TC", ) + create_loan_type( + "Term Loan Type 1", + 12000, + 7.5, + is_term_loan=1, + mode_of_payment="Cash", + disbursement_account="Disbursement Account - _TC", + payment_account="Payment Account - _TC", + loan_account="Loan Account - _TC", + interest_income_account="Interest Income Account - _TC", + penalty_income_account="Penalty Income Account - _TC", + repayment_schedule_type="Monthly as per repayment start date", + ) + + create_loan_type( + "Term Loan Type 2", + 12000, + 7.5, + is_term_loan=1, + mode_of_payment="Cash", + disbursement_account="Disbursement Account - _TC", + payment_account="Payment Account - _TC", + loan_account="Loan Account - _TC", + interest_income_account="Interest Income Account - _TC", + penalty_income_account="Penalty Income Account - _TC", + repayment_schedule_type="Pro-rated calendar months", + repayment_date_on="Start of the next month", + ) + + create_loan_type( + "Term Loan Type 3", + 12000, + 7.5, + is_term_loan=1, + mode_of_payment="Cash", + disbursement_account="Disbursement Account - _TC", + payment_account="Payment Account - _TC", + loan_account="Loan Account - _TC", + interest_income_account="Interest Income Account - _TC", + penalty_income_account="Penalty Income Account - _TC", + repayment_schedule_type="Pro-rated calendar months", + repayment_date_on="End of the current month", + ) + create_loan_type( "Stock Loan", 2000000, @@ -902,6 +955,45 @@ class TestLoan(unittest.TestCase): amounts = calculate_amounts(loan.name, add_days(last_date, 5)) self.assertEqual(flt(amounts["pending_principal_amount"], 0), 0) + def test_term_loan_schedule_types(self): + loan = create_loan( + self.applicant1, + "Term Loan Type 1", + 12000, + "Repay Over Number of Periods", + 12, + repayment_start_date="2022-10-17", + ) + + # Check for first, second and last installment date + self.assertEqual(format_date(loan.get("repayment_schedule")[0].payment_date), "17-10-2022") + self.assertEqual(format_date(loan.get("repayment_schedule")[1].payment_date), "17-11-2022") + self.assertEqual(format_date(loan.get("repayment_schedule")[-1].payment_date), "17-09-2023") + + loan.loan_type = "Term Loan Type 2" + loan.save() + + # Check for first, second and last installment date + self.assertEqual(format_date(loan.get("repayment_schedule")[0].payment_date), "01-11-2022") + self.assertEqual(format_date(loan.get("repayment_schedule")[1].payment_date), "01-12-2022") + self.assertEqual(format_date(loan.get("repayment_schedule")[-1].payment_date), "01-10-2023") + + loan.loan_type = "Term Loan Type 3" + loan.save() + + # Check for first, second and last installment date + self.assertEqual(format_date(loan.get("repayment_schedule")[0].payment_date), "31-10-2022") + self.assertEqual(format_date(loan.get("repayment_schedule")[1].payment_date), "30-11-2022") + self.assertEqual(format_date(loan.get("repayment_schedule")[-1].payment_date), "30-09-2023") + + loan.repayment_method = "Repay Fixed Amount per Period" + loan.monthly_repayment_amount = 1042 + loan.save() + + self.assertEqual(format_date(loan.get("repayment_schedule")[0].payment_date), "31-10-2022") + self.assertEqual(format_date(loan.get("repayment_schedule")[1].payment_date), "30-11-2022") + self.assertEqual(format_date(loan.get("repayment_schedule")[-1].payment_date), "30-09-2023") + def create_loan_scenario_for_penalty(doc): pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}] @@ -1033,6 +1125,8 @@ def create_loan_type( penalty_income_account=None, repayment_method=None, repayment_periods=None, + repayment_schedule_type=None, + repayment_date_on=None, ): if not frappe.db.exists("Loan Type", loan_name): @@ -1057,8 +1151,14 @@ def create_loan_type( "repayment_periods": repayment_periods, "write_off_amount": 100, } - ).insert() + ) + if loan_type.is_term_loan: + loan_type.repayment_schedule_type = repayment_schedule_type + if loan_type.repayment_schedule_type != "Monthly as per repayment start date": + loan_type.repayment_date_on = repayment_date_on + + loan_type.insert() loan_type.submit() diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.json b/erpnext/loan_management/doctype/loan_type/loan_type.json index e1ed3caf8f..5cc9464585 100644 --- a/erpnext/loan_management/doctype/loan_type/loan_type.json +++ b/erpnext/loan_management/doctype/loan_type/loan_type.json @@ -17,6 +17,7 @@ "is_term_loan", "disabled", "repayment_schedule_type", + "repayment_date_on", "description", "account_details_section", "mode_of_payment", @@ -161,17 +162,27 @@ }, { "depends_on": "is_term_loan", + "description": "The schedule type that will be used for generating the term loan schedules (will affect the payment date and monthly repayment amount)", "fieldname": "repayment_schedule_type", "fieldtype": "Select", "label": "Repayment Schedule Type", "mandatory_depends_on": "is_term_loan", "options": "\nMonthly as per repayment start date\nPro-rated calendar months" + }, + { + "depends_on": "eval:doc.repayment_schedule_type == \"Pro-rated calendar months\"", + "description": "Select whether the repayment date should be the end of the current month or start of the upcoming month", + "fieldname": "repayment_date_on", + "fieldtype": "Select", + "label": "Repayment Date On", + "mandatory_depends_on": "eval:doc.repayment_schedule_type == \"Pro-rated calendar months\"", + "options": "\nStart of the next month\nEnd of the current month" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-09-28 21:31:01.278941", + "modified": "2022-10-22 17:43:03.954201", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Type", From e59b147a620c45d8727c8a0adf9ea1cd0d57df6f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 23 Oct 2022 18:51:51 +0530 Subject: [PATCH 0392/1047] chore: Update tests --- .../loan_management/doctype/loan/test_loan.py | 50 ++++++++++++++----- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index 0e61161601..388e65d9e5 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -56,6 +56,7 @@ class TestLoan(unittest.TestCase): loan_account="Loan Account - _TC", interest_income_account="Interest Income Account - _TC", penalty_income_account="Penalty Income Account - _TC", + repayment_schedule_type="Monthly as per repayment start date", ) create_loan_type( @@ -115,6 +116,7 @@ class TestLoan(unittest.TestCase): "Loan Account - _TC", "Interest Income Account - _TC", "Penalty Income Account - _TC", + repayment_schedule_type="Monthly as per repayment start date", ) create_loan_type( @@ -966,33 +968,57 @@ class TestLoan(unittest.TestCase): ) # Check for first, second and last installment date - self.assertEqual(format_date(loan.get("repayment_schedule")[0].payment_date), "17-10-2022") - self.assertEqual(format_date(loan.get("repayment_schedule")[1].payment_date), "17-11-2022") - self.assertEqual(format_date(loan.get("repayment_schedule")[-1].payment_date), "17-09-2023") + self.assertEqual( + format_date(loan.get("repayment_schedule")[0].payment_date, "dd-MM-yyyy"), "17-10-2022" + ) + self.assertEqual( + format_date(loan.get("repayment_schedule")[1].payment_date, "dd-MM-yyyy"), "17-11-2022" + ) + self.assertEqual( + format_date(loan.get("repayment_schedule")[-1].payment_date, "dd-MM-yyyy"), "17-09-2023" + ) loan.loan_type = "Term Loan Type 2" loan.save() # Check for first, second and last installment date - self.assertEqual(format_date(loan.get("repayment_schedule")[0].payment_date), "01-11-2022") - self.assertEqual(format_date(loan.get("repayment_schedule")[1].payment_date), "01-12-2022") - self.assertEqual(format_date(loan.get("repayment_schedule")[-1].payment_date), "01-10-2023") + self.assertEqual( + format_date(loan.get("repayment_schedule")[0].payment_date, "dd-MM-yyyy"), "01-11-2022" + ) + self.assertEqual( + format_date(loan.get("repayment_schedule")[1].payment_date, "dd-MM-yyyy"), "01-12-2022" + ) + self.assertEqual( + format_date(loan.get("repayment_schedule")[-1].payment_date, "dd-MM-yyyy"), "01-10-2023" + ) loan.loan_type = "Term Loan Type 3" loan.save() # Check for first, second and last installment date - self.assertEqual(format_date(loan.get("repayment_schedule")[0].payment_date), "31-10-2022") - self.assertEqual(format_date(loan.get("repayment_schedule")[1].payment_date), "30-11-2022") - self.assertEqual(format_date(loan.get("repayment_schedule")[-1].payment_date), "30-09-2023") + self.assertEqual( + format_date(loan.get("repayment_schedule")[0].payment_date, "dd-MM-yyyy"), "31-10-2022" + ) + self.assertEqual( + format_date(loan.get("repayment_schedule")[1].payment_date, "dd-MM-yyyy"), "30-11-2022" + ) + self.assertEqual( + format_date(loan.get("repayment_schedule")[-1].payment_date, "dd-MM-yyyy"), "30-09-2023" + ) loan.repayment_method = "Repay Fixed Amount per Period" loan.monthly_repayment_amount = 1042 loan.save() - self.assertEqual(format_date(loan.get("repayment_schedule")[0].payment_date), "31-10-2022") - self.assertEqual(format_date(loan.get("repayment_schedule")[1].payment_date), "30-11-2022") - self.assertEqual(format_date(loan.get("repayment_schedule")[-1].payment_date), "30-09-2023") + self.assertEqual( + format_date(loan.get("repayment_schedule")[0].payment_date, "dd-MM-yyyy"), "31-10-2022" + ) + self.assertEqual( + format_date(loan.get("repayment_schedule")[1].payment_date, "dd-MM-yyyy"), "30-11-2022" + ) + self.assertEqual( + format_date(loan.get("repayment_schedule")[-1].payment_date, "dd-MM-yyyy"), "30-09-2023" + ) def create_loan_scenario_for_penalty(doc): From 1105e520315cbee4915e696706ecc3bfa1f0af8f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 23 Oct 2022 22:55:08 +0530 Subject: [PATCH 0393/1047] chore: Update allow on submit fields --- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index b98cd3ad67..892fa173c5 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1036,6 +1036,7 @@ "read_only": 1 }, { + "allow_on_submit": 1, "depends_on": "redeem_loyalty_points", "fieldname": "loyalty_redemption_account", "fieldtype": "Link", @@ -1334,6 +1335,7 @@ "options": "fa fa-money" }, { + "allow_on_submit": 1, "depends_on": "is_pos", "fieldname": "cash_bank_account", "fieldtype": "Link", @@ -1433,6 +1435,7 @@ "print_hide": 1 }, { + "allow_on_submit": 1, "depends_on": "is_pos", "fieldname": "account_for_change_amount", "fieldtype": "Link", @@ -1481,6 +1484,7 @@ "hide_seconds": 1 }, { + "allow_on_submit": 1, "fieldname": "write_off_account", "fieldtype": "Link", "hide_days": 1, @@ -1919,6 +1923,7 @@ "read_only": 1 }, { + "allow_on_submit": 1, "depends_on": "eval:doc.is_internal_customer", "description": "Unrealized Profit / Loss account for intra-company transfers", "fieldname": "unrealized_profit_loss_account", @@ -1961,6 +1966,7 @@ "label": "Disable Rounded Total" }, { + "allow_on_submit": 1, "fieldname": "additional_discount_account", "fieldtype": "Link", "label": "Discount Account", @@ -2119,7 +2125,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2022-10-15 19:15:49.526529", + "modified": "2022-10-23 10:52:47.416251", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", From ed98015a5639889086a7662256bf743dfe387ffc Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 23 Oct 2022 23:03:50 +0530 Subject: [PATCH 0394/1047] test: Add unit tests --- .../doctype/sales_invoice/sales_invoice.py | 96 ++++++++++--------- .../sales_invoice/test_sales_invoice.py | 26 +++++ 2 files changed, 76 insertions(+), 46 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 1047e88606..e2ed9d3501 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -521,52 +521,53 @@ class SalesInvoice(SellingController): self.set_paid_amount() def on_update_after_submit(self): - needs_repost = 0 + if hasattr(self, "repost_required"): + needs_repost = 0 - # Check if any field affecting accounting entry is altered - doc_before_update = self.get_doc_before_save() - accounting_dimensions = get_accounting_dimensions() + ["cost_center", "project"] + # Check if any field affecting accounting entry is altered + doc_before_update = self.get_doc_before_save() + accounting_dimensions = get_accounting_dimensions() + ["cost_center", "project"] - # Check if opening entry check updated - if doc_before_update.get("is_opening") != self.is_opening: - needs_repost = 1 - - if not needs_repost: - # Parent Level Accounts excluding party account - for field in ( - "additional_discount_account", - "cash_bank_account", - "account_for_change_amount", - "write_off_account", - "loyalty_redemption_account", - "unrealized_profit_loss_account", - ): - if doc_before_update.get(field) != self.get(field): - needs_repost = 1 - break - - # Check for parent accounting dimensions - for dimension in accounting_dimensions: - if doc_before_update.get(dimension) != self.get(dimension): - needs_repost = 1 - break - - # Check for child tables - if self.check_if_child_table_updated( - "items", - doc_before_update, - ("income_account", "expense_account", "discount_account"), - accounting_dimensions, - ): + # Check if opening entry check updated + if doc_before_update.get("is_opening") != self.is_opening: needs_repost = 1 - if self.check_if_child_table_updated( - "taxes", doc_before_update, ("account_head",), accounting_dimensions - ): - needs_repost = 1 + if not needs_repost: + # Parent Level Accounts excluding party account + for field in ( + "additional_discount_account", + "cash_bank_account", + "account_for_change_amount", + "write_off_account", + "loyalty_redemption_account", + "unrealized_profit_loss_account", + ): + if doc_before_update.get(field) != self.get(field): + needs_repost = 1 + break - self.validate_accounts() - self.db_set("repost_required", needs_repost) + # Check for parent accounting dimensions + for dimension in accounting_dimensions: + if doc_before_update.get(dimension) != self.get(dimension): + needs_repost = 1 + break + + # Check for child tables + if self.check_if_child_table_updated( + "items", + doc_before_update, + ("income_account", "expense_account", "discount_account"), + accounting_dimensions, + ): + needs_repost = 1 + + if self.check_if_child_table_updated( + "taxes", doc_before_update, ("account_head",), accounting_dimensions + ): + needs_repost = 1 + + self.validate_accounts() + self.db_set("repost_required", needs_repost) def check_if_child_table_updated( self, child_table, doc_before_update, fields_to_check, accounting_dimensions @@ -585,11 +586,14 @@ class SalesInvoice(SellingController): @frappe.whitelist() def repost_accounting_entries(self): - self.docstatus = 2 - self.make_gl_entries_on_cancel() - self.docstatus = 1 - self.make_gl_entries() - self.db_set("repost_required", 0) + if self.repost_required: + self.docstatus = 2 + self.make_gl_entries_on_cancel() + self.docstatus = 1 + self.make_gl_entries() + self.db_set("repost_required", 0) + else: + frappe.throw(_("No updates pending for reposting")) def set_paid_amount(self): paid_amount = 0.0 diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 301d3e136e..ed04747264 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2728,6 +2728,31 @@ class TestSalesInvoice(unittest.TestCase): check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1)) + # Update Invoice post submit and then check GL Entries again + + si.load_from_db() + si.items[0].income_account = "Service - _TC" + si.additional_discount_account = "_Test Account Sales - _TC" + si.taxes[0].account_head = "VAT 5% - _TC" + si.save() + + si.load_from_db() + self.assertTrue(si.repost_required) + + si.repost_accounting_entries() + + expected_gle = [ + ["_Test Account Sales - _TC", 22.0, 0.0, nowdate()], + ["Debtors - _TC", 88, 0.0, nowdate()], + ["Service - _TC", 0.0, 100.0, nowdate()], + ["VAT 5% - _TC", 0.0, 10.0, nowdate()], + ] + + check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1)) + + si.load_from_db() + self.assertFalse(si.repost_required) + def test_asset_depreciation_on_sale_with_pro_rata(self): """ Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale. @@ -3269,6 +3294,7 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date): """select account, debit, credit, posting_date from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s and posting_date > %s + and is_cancelled = 0 order by posting_date asc, account asc""", (voucher_no, posting_date), as_dict=1, From 1a980123a2c83c669735a87d1d59815666de6170 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 24 Oct 2022 10:08:55 +0530 Subject: [PATCH 0395/1047] chore: Update tests --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index ed04747264..d6e05fb91b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2733,7 +2733,7 @@ class TestSalesInvoice(unittest.TestCase): si.load_from_db() si.items[0].income_account = "Service - _TC" si.additional_discount_account = "_Test Account Sales - _TC" - si.taxes[0].account_head = "VAT 5% - _TC" + si.taxes[0].account_head = "TDS Payable - _TC" si.save() si.load_from_db() @@ -2745,7 +2745,7 @@ class TestSalesInvoice(unittest.TestCase): ["_Test Account Sales - _TC", 22.0, 0.0, nowdate()], ["Debtors - _TC", 88, 0.0, nowdate()], ["Service - _TC", 0.0, 100.0, nowdate()], - ["VAT 5% - _TC", 0.0, 10.0, nowdate()], + ["TDS Payable - _TC", 0.0, 10.0, nowdate()], ] check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1)) From 267e7c3a9001c56bf7c1af88e2dfdef175e0502f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 24 Oct 2022 11:45:24 +0530 Subject: [PATCH 0396/1047] fix: Clear invoice table post importing invoices --- .../opening_invoice_creation_tool.js | 13 +++---------- .../opening_invoice_creation_tool.py | 2 -- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js index 7eb5c4234d..1f4166151a 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js @@ -22,13 +22,13 @@ frappe.ui.form.on('Opening Invoice Creation Tool', { } if (data.user != frappe.session.user) return; if (data.count == data.total) { - setTimeout((title) => { + setTimeout(() => { frm.doc.import_in_progress = false; frm.clear_table("invoices"); frm.refresh_fields(); frm.page.clear_indicator(); - frm.dashboard.hide_progress(title); - frappe.msgprint(__("Opening {0} Invoice created", [frm.doc.invoice_type])); + frm.dashboard.hide_progress(); + frappe.msgprint(__("Opening {0} Invoices created", [frm.doc.invoice_type])); }, 1500, data.title); return; } @@ -51,13 +51,6 @@ frappe.ui.form.on('Opening Invoice Creation Tool', { method: "make_invoices", freeze: 1, freeze_message: __("Creating {0} Invoice", [frm.doc.invoice_type]), - callback: function(r) { - if (r.message.length == 1) { - frappe.msgprint(__("{0} Invoice created successfully.", [frm.doc.invoice_type])); - } else if (r.message.length < 50) { - frappe.msgprint(__("{0} Invoices created successfully.", [frm.doc.invoice_type])); - } - } }); }); diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py index f7df1ff4a1..57fe4059c7 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py @@ -255,8 +255,6 @@ def start_import(invoices): def publish(index, total, doctype): - if total < 50: - return frappe.publish_realtime( "opening_invoice_creation_progress", dict( From 46d148defd59cbb1b9147e035aa6de41ee5fd099 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 24 Oct 2022 15:48:34 +0530 Subject: [PATCH 0397/1047] fix: searchfield not working for cuctsomer, supplier as per customize form --- erpnext/controllers/queries.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 8eae0a0702..691a39aa8b 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -83,13 +83,11 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): conditions = [] cust_master_name = frappe.defaults.get_user_default("cust_master_name") - if cust_master_name == "Customer Name": - fields = ["name", "customer_group", "territory"] - else: - fields = ["name", "customer_name", "customer_group", "territory"] + fields = ["name"] + if cust_master_name != "Customer Name": + fields = ["customer_name"] fields = get_fields(doctype, fields) - searchfields = frappe.get_meta(doctype).get_search_fields() searchfields = " or ".join(field + " like %(txt)s" for field in searchfields) @@ -122,10 +120,9 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): doctype = "Supplier" supp_master_name = frappe.defaults.get_user_default("supp_master_name") - if supp_master_name == "Supplier Name": - fields = ["name", "supplier_group"] - else: - fields = ["name", "supplier_name", "supplier_group"] + fields = ["name"] + if supp_master_name != "Supplier Name": + fields = ["supplier_name"] fields = get_fields(doctype, fields) From 5f84993bae5df78e257cc2bfc41c123a1122a0b6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 24 Oct 2022 16:10:47 +0530 Subject: [PATCH 0398/1047] test: added test case to validate seachfields for customer, supplier --- .../buying/doctype/supplier/test_supplier.py | 35 +++++++++++++++++++ erpnext/controllers/queries.py | 6 ++-- .../selling/doctype/customer/test_customer.py | 28 +++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py index 55722686fe..e2dbf21be2 100644 --- a/erpnext/buying/doctype/supplier/test_supplier.py +++ b/erpnext/buying/doctype/supplier/test_supplier.py @@ -3,6 +3,7 @@ import frappe +from frappe.custom.doctype.property_setter.property_setter import make_property_setter from frappe.test_runner import make_test_records from erpnext.accounts.party import get_due_date @@ -152,6 +153,40 @@ class TestSupplier(FrappeTestCase): # Rollback address.delete() + def test_serach_fields_for_supplier(self): + from erpnext.controllers.queries import supplier_query + + supplier_name = create_supplier(supplier_name="Test Supplier 1").name + + make_property_setter( + "Supplier", None, "search_fields", "supplier_group", "Data", for_doctype="Doctype" + ) + + data = supplier_query( + "Supplier", supplier_name, "name", 0, 20, filters={"name": supplier_name}, as_dict=True + ) + + self.assertEqual(data[0].name, supplier_name) + self.assertEqual(data[0].supplier_group, "Services") + self.assertTrue("supplier_type" not in data[0]) + + make_property_setter( + "Supplier", + None, + "search_fields", + "supplier_group, supplier_type", + "Data", + for_doctype="Doctype", + ) + data = supplier_query( + "Supplier", supplier_name, "name", 0, 20, filters={"name": supplier_name}, as_dict=True + ) + + self.assertEqual(data[0].name, supplier_name) + self.assertEqual(data[0].supplier_group, "Services") + self.assertEqual(data[0].supplier_type, "Company") + self.assertTrue("supplier_type" in data[0]) + def create_supplier(**args): args = frappe._dict(args) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 691a39aa8b..3bdc017068 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -78,7 +78,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs -def customer_query(doctype, txt, searchfield, start, page_len, filters): +def customer_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): doctype = "Customer" conditions = [] cust_master_name = frappe.defaults.get_user_default("cust_master_name") @@ -110,13 +110,14 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): } ), {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len}, + as_dict=as_dict, ) # searches for supplier @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs -def supplier_query(doctype, txt, searchfield, start, page_len, filters): +def supplier_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): doctype = "Supplier" supp_master_name = frappe.defaults.get_user_default("supp_master_name") @@ -142,6 +143,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): **{"field": ", ".join(fields), "key": searchfield, "mcond": get_match_cond(doctype)} ), {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len}, + as_dict=as_dict, ) diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 7dc3fab623..691adccd4d 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -3,6 +3,7 @@ import frappe +from frappe.custom.doctype.property_setter.property_setter import make_property_setter from frappe.test_runner import make_test_records from frappe.tests.utils import FrappeTestCase from frappe.utils import flt @@ -341,6 +342,33 @@ class TestCustomer(FrappeTestCase): due_date = get_due_date("2017-01-22", "Customer", "_Test Customer") self.assertEqual(due_date, "2017-01-22") + def test_serach_fields_for_customer(self): + from erpnext.controllers.queries import customer_query + + make_property_setter( + "Customer", None, "search_fields", "customer_group", "Data", for_doctype="Doctype" + ) + + data = customer_query( + "Customer", "_Test Customer", "", 0, 20, filters={"name": "_Test Customer"}, as_dict=True + ) + + self.assertEqual(data[0].name, "_Test Customer") + self.assertEqual(data[0].customer_group, "_Test Customer Group") + self.assertTrue("territory" not in data[0]) + + make_property_setter( + "Customer", None, "search_fields", "customer_group, territory", "Data", for_doctype="Doctype" + ) + data = customer_query( + "Customer", "_Test Customer", "", 0, 20, filters={"name": "_Test Customer"}, as_dict=True + ) + + self.assertEqual(data[0].name, "_Test Customer") + self.assertEqual(data[0].customer_group, "_Test Customer Group") + self.assertEqual(data[0].territory, "_Test Territory") + self.assertTrue("territory" in data[0]) + def get_customer_dict(customer_name): return { From 49ee8736558d4c788ebb70cfddbd116e18d1e4b2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 24 Oct 2022 17:33:44 +0530 Subject: [PATCH 0399/1047] fix: Curreny in SOA print for multi-currency party --- .../accounts/report/general_ledger/general_ledger.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index 378fa3791c..7e1df267b1 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -52,22 +52,22 @@ {% } %} - {%= format_currency(data[i].debit, filters.presentation_currency) %} + {%= format_currency(data[i].debit, data[i].account_currency) %} - {%= format_currency(data[i].credit, filters.presentation_currency) %} + {%= format_currency(data[i].credit, data[i].account_currency) %} {% } else { %} {%= frappe.format(data[i].account, {fieldtype: "Link"}) || " " %} - {%= data[i].account && format_currency(data[i].debit, filters.presentation_currency) %} + {%= data[i].account && format_currency(data[i].debit, data[i].account_currency) %} - {%= data[i].account && format_currency(data[i].credit, filters.presentation_currency) %} + {%= data[i].account && format_currency(data[i].credit, data[i].account_currency) %} {% } %} - {%= format_currency(data[i].balance, filters.presentation_currency) %} + {%= format_currency(data[i].balance, data[i].account_currency) %} {% } %} From a18a715bb47c69578a435af326c0efb9e0baec23 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 24 Oct 2022 19:13:02 +0530 Subject: [PATCH 0400/1047] chore: Use account currency as fallback --- .../accounts/report/general_ledger/general_ledger.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index 7e1df267b1..c04f518d7e 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -52,22 +52,22 @@ {% } %} - {%= format_currency(data[i].debit, data[i].account_currency) %} + {%= format_currency(data[i].debit, filters.presentation_currency || data[i].account_currency) %} - {%= format_currency(data[i].credit, data[i].account_currency) %} + {%= format_currency(data[i].credit, filters.presentation_currency || data[i].account_currency) %} {% } else { %} {%= frappe.format(data[i].account, {fieldtype: "Link"}) || " " %} - {%= data[i].account && format_currency(data[i].debit, data[i].account_currency) %} + {%= data[i].account && format_currency(data[i].debit, filters.presentation_currency || data[i].account_currency) %} - {%= data[i].account && format_currency(data[i].credit, data[i].account_currency) %} + {%= data[i].account && format_currency(data[i].credit, filters.presentation_currency || data[i].account_currency) %} {% } %} - {%= format_currency(data[i].balance, data[i].account_currency) %} + {%= format_currency(data[i].balance, filters.presentation_currency || data[i].account_currency) %} {% } %} From 397e3b1ade6a52d542b27ac3a02165acf8797f41 Mon Sep 17 00:00:00 2001 From: niralisatapara Date: Tue, 25 Oct 2022 15:09:59 +0530 Subject: [PATCH 0401/1047] feat: item wise tds calculation item wise tds calculation --- .../purchase_invoice_item.json | 7 ++++++ .../tax_withholding_category.py | 12 +++++----- erpnext/controllers/taxes_and_totals.py | 1 + erpnext/patches/v14_0/update_tds_fields.py | 23 ++++++++++++------- 4 files changed, 29 insertions(+), 14 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 9de9036887..0a39a62ef1 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -40,6 +40,7 @@ "discount_amount", "base_rate_with_margin", "sec_break2", + "apply_tds", "rate", "amount", "item_tax_template", @@ -866,6 +867,12 @@ "label": "Product Bundle", "options": "Product Bundle", "read_only": 1 + }, + { + "default": "1", + "fieldname": "apply_tds", + "fieldtype": "Check", + "label": "Apply TDS" } ], "idx": 1, 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 280cc24e2c..b4f46cca30 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -245,7 +245,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N if party_type == "Supplier": ldc = get_lower_deduction_certificate(tax_details, pan_no) if tax_deducted: - net_total = inv.net_total + net_total = inv.tax_withholding_net_total if ldc: tax_amount = get_tds_amount_from_ldc( ldc, parties, pan_no, tax_details, posting_date, net_total @@ -395,7 +395,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers): tds_amount = 0 invoice_filters = {"name": ("in", vouchers), "docstatus": 1, "apply_tds": 1} - field = "sum(net_total)" + field = "sum(tax_withholding_net_total)" if cint(tax_details.consider_party_ledger_amount): invoice_filters.pop("apply_tds", None) @@ -418,12 +418,12 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers): ) supp_credit_amt += supp_jv_credit_amt - supp_credit_amt += inv.net_total + supp_credit_amt += inv.tax_withholding_net_total threshold = tax_details.get("threshold", 0) cumulative_threshold = tax_details.get("cumulative_threshold", 0) - if (threshold and inv.net_total >= threshold) or ( + if (threshold and inv.tax_withholding_net_total >= threshold) or ( cumulative_threshold and supp_credit_amt >= cumulative_threshold ): if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint( @@ -443,7 +443,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers): ldc.valid_upto, inv.get("posting_date") or inv.get("transaction_date"), tax_deducted, - inv.net_total, + inv.tax_withholding_net_total, ldc.certificate_limit, ): tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details) @@ -526,7 +526,7 @@ def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net limit_consumed = frappe.db.get_value( "Purchase Invoice", {"supplier": ("in", parties), "apply_tds": 1, "docstatus": 1}, - "sum(net_total)", + "sum(tax_withholding_net_total)", ) if is_valid_certificate( diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index ee19adc425..42da4cf181 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -58,6 +58,7 @@ class calculate_taxes_and_totals(object): self.initialize_taxes() self.determine_exclusive_rate() self.calculate_net_total() + self.calculate_tax_withholding_net_total() self.calculate_taxes() self.manipulate_grand_total_for_inclusive_tax() self.calculate_totals() diff --git a/erpnext/patches/v14_0/update_tds_fields.py b/erpnext/patches/v14_0/update_tds_fields.py index a8358c3a5d..98feee2ac7 100644 --- a/erpnext/patches/v14_0/update_tds_fields.py +++ b/erpnext/patches/v14_0/update_tds_fields.py @@ -1,7 +1,8 @@ import frappe def execute(): - frappe.db.sql(""" + frappe.db.sql( + """ UPDATE `tabPurchase Invoice Item` INNER JOIN @@ -13,11 +14,17 @@ def execute(): WHERE `tabPurchase Invoice`.apply_tds = 1 and `tabPurchase Invoice`.docstatus = 1 - """) + """ + ) - frappe.db.sql(""" - UPDATE `tabPurchase Invoice` - SET tax_withholding_net_total = net_total, - base_tax_withholding_net_total = base_net_total - WHERE apply_tds = 1 and docstatus = 1""") - + frappe.db.sql( + """ + UPDATE + `tabPurchase Invoice` + SET + tax_withholding_net_total = net_total, + base_tax_withholding_net_total = base_net_total + WHERE + apply_tds = 1 and docstatus = 1 + """ + ) From a963618b08f90408bdcaeaa033cb7cbe1f481232 Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Tue, 25 Oct 2022 21:54:55 -0600 Subject: [PATCH 0402/1047] fix: add translate function to name of chart labels in deferred_revenue_and_expense.py --- .../deferred_revenue_and_expense.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py index 1eb257ac85..6cc86c3efe 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py @@ -396,7 +396,7 @@ class Deferred_Revenue_and_Expense_Report(object): "labels": [period.label for period in self.period_list], "datasets": [ { - "name": "Actual Posting", + "name": _("Actual Posting"), "chartType": "bar", "values": [x.actual for x in self.period_total], } @@ -410,7 +410,7 @@ class Deferred_Revenue_and_Expense_Report(object): if self.filters.with_upcoming_postings: chart["data"]["datasets"].append( - {"name": "Expected", "chartType": "line", "values": [x.total for x in self.period_total]} + {"name": _("Expected"), "chartType": "line", "values": [x.total for x in self.period_total]} ) return chart From a671652ab2ebb5d25aa45d979d1c3c8b3861efcf Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Tue, 25 Oct 2022 22:00:46 -0600 Subject: [PATCH 0403/1047] fix: refactor code for better translatable string --- .../accounts/report/accounts_receivable/accounts_receivable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 8557c03bd5..7fe23de612 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -1009,7 +1009,7 @@ class ReceivablePayableReport(object): "{range3}-{range4}".format( range3=cint(self.filters["range3"]) + 1, range4=self.filters["range4"] ), - "{range4}-{above}".format(range4=cint(self.filters["range4"]) + 1, above=_("Above")), + _("{range4}-Above").format(range4=cint(self.filters["range4"]) + 1), ] ): self.add_column(label=label, fieldname="range" + str(i + 1)) From 71a0ae2e596538de3cac07839d4e03cf24f7e1bd Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Tue, 25 Oct 2022 22:05:25 -0600 Subject: [PATCH 0404/1047] fix: refactor code for better translatable string in stock_ageing.py --- erpnext/stock/report/stock_ageing/stock_ageing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 7c430e491a..944433103f 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -198,11 +198,11 @@ def setup_ageing_columns(filters: Filters, range_columns: List): f"0 - {filters['range1']}", f"{cint(filters['range1']) + 1} - {cint(filters['range2'])}", f"{cint(filters['range2']) + 1} - {cint(filters['range3'])}", - f"{cint(filters['range3']) + 1} - {_('Above')}", + _("{0} - Above").format(cint(filters['range3']) + 1), ] for i, label in enumerate(ranges): fieldname = "range" + str(i + 1) - add_column(range_columns, label=f"Age ({label})", fieldname=fieldname) + add_column(range_columns, label=_("Age ({0})").format(label), fieldname=fieldname) def add_column( From c1e608d9ef84b4b9e765b84482b666554c98220c Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Tue, 25 Oct 2022 22:09:02 -0600 Subject: [PATCH 0405/1047] fix: add translate function to period in stock_analytics.py --- erpnext/stock/report/stock_analytics/stock_analytics.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py index 89ca9d9126..175c479dda 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.py +++ b/erpnext/stock/report/stock_analytics/stock_analytics.py @@ -114,11 +114,11 @@ def get_period(posting_date, filters): months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] if filters.range == "Weekly": - period = "Week " + str(posting_date.isocalendar()[1]) + " " + str(posting_date.year) + period = _("Week {0} {1}").format(str(posting_date.isocalendar()[1]), str(posting_date.year)) elif filters.range == "Monthly": - period = str(months[posting_date.month - 1]) + " " + str(posting_date.year) + period = _(str(months[posting_date.month - 1])) + " " + str(posting_date.year) elif filters.range == "Quarterly": - period = "Quarter " + str(((posting_date.month - 1) // 3) + 1) + " " + str(posting_date.year) + period = _("Quarter {0} {1}").format(str(((posting_date.month - 1) // 3) + 1), str(posting_date.year)) else: year = get_fiscal_year(posting_date, company=filters.company) period = str(year[2]) From 083a78135c04af032ca5665e6b451876a4b758ed Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Tue, 25 Oct 2022 22:17:06 -0600 Subject: [PATCH 0406/1047] fix: add translate function to period in sales_analytics.py --- erpnext/selling/report/sales_analytics/sales_analytics.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 186352848d..2c34c23dab 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -313,11 +313,11 @@ class Analytics(object): def get_period(self, posting_date): if self.filters.range == "Weekly": - period = "Week " + str(posting_date.isocalendar()[1]) + " " + str(posting_date.year) + period = _("Week {0} {1}").format(str(posting_date.isocalendar()[1]), str(posting_date.year)) elif self.filters.range == "Monthly": - period = str(self.months[posting_date.month - 1]) + " " + str(posting_date.year) + period = _(str(self.months[posting_date.month - 1])) + " " + str(posting_date.year) elif self.filters.range == "Quarterly": - period = "Quarter " + str(((posting_date.month - 1) // 3) + 1) + " " + str(posting_date.year) + period = _("Quarter {0} {1}").format(str(((posting_date.month - 1) // 3) + 1), str(posting_date.year)) else: year = get_fiscal_year(posting_date, company=self.filters.company) period = str(year[0]) From 48ed6381b374032a3d46f4afb3f3252ba83c2580 Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Tue, 25 Oct 2022 22:18:47 -0600 Subject: [PATCH 0407/1047] fix: add translate function to name of chart labels in budget_variance_report.py --- .../report/budget_variance_report/budget_variance_report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 7b774ba740..96cfab9f11 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -383,8 +383,8 @@ def get_chart_data(filters, columns, data): "data": { "labels": labels, "datasets": [ - {"name": "Budget", "chartType": "bar", "values": budget_values}, - {"name": "Actual Expense", "chartType": "bar", "values": actual_values}, + {"name": _("Budget"), "chartType": "bar", "values": budget_values}, + {"name": _("Actual Expense"), "chartType": "bar", "values": actual_values}, ], }, "type": "bar", From 2012bdf4bd8d1cead09de99e5b9a68aa8a9990ef Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Tue, 25 Oct 2022 22:24:33 -0600 Subject: [PATCH 0408/1047] fix: add translate function to string on budget_variance_report.js to match the variance word translated --- .../report/budget_variance_report/budget_variance_report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js index 718b6e2fcb..5955c2e0fc 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js @@ -75,7 +75,7 @@ frappe.query_reports["Budget Variance Report"] = { "formatter": function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - if (column.fieldname.includes('variance')) { + if (column.fieldname.includes(__("variance"))) { if (data[column.fieldname] < 0) { value = "" + value + ""; From b7b53b58574de501410a80fab02073291d9a5fc4 Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Tue, 25 Oct 2022 22:34:21 -0600 Subject: [PATCH 0409/1047] fix: correct linters --- erpnext/selling/report/sales_analytics/sales_analytics.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 2c34c23dab..605d2fac44 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -317,7 +317,9 @@ class Analytics(object): elif self.filters.range == "Monthly": period = _(str(self.months[posting_date.month - 1])) + " " + str(posting_date.year) elif self.filters.range == "Quarterly": - period = _("Quarter {0} {1}").format(str(((posting_date.month - 1) // 3) + 1), str(posting_date.year)) + period = _("Quarter {0} {1}").format( + str(((posting_date.month - 1) // 3) + 1), str(posting_date.year) + ) else: year = get_fiscal_year(posting_date, company=self.filters.company) period = str(year[0]) From 9c529c61bb90ac7827e8448b90e33b812a9f7093 Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Tue, 25 Oct 2022 22:35:37 -0600 Subject: [PATCH 0410/1047] fix: correct linters --- erpnext/stock/report/stock_analytics/stock_analytics.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py index 175c479dda..27b94ab3f9 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.py +++ b/erpnext/stock/report/stock_analytics/stock_analytics.py @@ -118,7 +118,9 @@ def get_period(posting_date, filters): elif filters.range == "Monthly": period = _(str(months[posting_date.month - 1])) + " " + str(posting_date.year) elif filters.range == "Quarterly": - period = _("Quarter {0} {1}").format(str(((posting_date.month - 1) // 3) + 1), str(posting_date.year)) + period = _("Quarter {0} {1}").format( + str(((posting_date.month - 1) // 3) + 1), str(posting_date.year) + ) else: year = get_fiscal_year(posting_date, company=filters.company) period = str(year[2]) From 4c7fa9482d81ec0794c69570371e4b923d188f5f Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Tue, 25 Oct 2022 22:39:05 -0600 Subject: [PATCH 0411/1047] fix: correct linters --- erpnext/stock/report/stock_ageing/stock_ageing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 944433103f..2fa97ae354 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -198,7 +198,7 @@ def setup_ageing_columns(filters: Filters, range_columns: List): f"0 - {filters['range1']}", f"{cint(filters['range1']) + 1} - {cint(filters['range2'])}", f"{cint(filters['range2']) + 1} - {cint(filters['range3'])}", - _("{0} - Above").format(cint(filters['range3']) + 1), + _("{0} - Above").format(cint(filters["range3"]) + 1), ] for i, label in enumerate(ranges): fieldname = "range" + str(i + 1) From f9f78c1086a5d19f9aef7370c85ca76b404e4c63 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 26 Oct 2022 11:02:14 +0530 Subject: [PATCH 0412/1047] fix: Company bank account filter in Bank Clearance --- .../doctype/bank_clearance/bank_clearance.js | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js index 63cc46518f..7e57c2fc47 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js @@ -4,6 +4,23 @@ frappe.ui.form.on("Bank Clearance", { setup: function(frm) { frm.add_fetch("account", "account_currency", "account_currency"); + + frm.set_query("account", function() { + return { + "filters": { + "account_type": ["in",["Bank","Cash"]], + "is_group": 0, + } + }; + }); + + frm.set_query("bank_account", function () { + return { + filters: { + 'is_company_account': 1 + }, + }; + }); }, onload: function(frm) { @@ -12,14 +29,7 @@ frappe.ui.form.on("Bank Clearance", { locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: ""; frm.set_value("account", default_bank_account); - frm.set_query("account", function() { - return { - "filters": { - "account_type": ["in",["Bank","Cash"]], - "is_group": 0 - } - }; - }); + frm.set_value("from_date", frappe.datetime.month_start()); frm.set_value("to_date", frappe.datetime.month_end()); From 4cd65027c4a80acce8251c3d3b274c3963daad17 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 26 Oct 2022 16:48:24 +0530 Subject: [PATCH 0413/1047] fix: Add condition for discount section collapse --- .../doctype/purchase_invoice_item/purchase_invoice_item.json | 4 +++- .../doctype/sales_invoice_item/sales_invoice_item.json | 3 ++- .../doctype/purchase_order_item/purchase_order_item.json | 3 ++- .../selling/doctype/sales_order_item/sales_order_item.json | 3 ++- .../stock/doctype/delivery_note_item/delivery_note_item.json | 3 ++- .../doctype/purchase_receipt_item/purchase_receipt_item.json | 3 ++- 6 files changed, 13 insertions(+), 6 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 9de9036887..a8f6f80b6b 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -214,6 +214,7 @@ "reqd": 1 }, { + "default": "1", "depends_on": "eval:doc.uom != doc.stock_uom", "fieldname": "conversion_factor", "fieldtype": "Float", @@ -820,6 +821,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount", "fieldname": "section_break_26", "fieldtype": "Section Break", "label": "Discount and Margin" @@ -871,7 +873,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-10-12 03:37:29.032732", + "modified": "2022-10-26 16:05:37.304788", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index a307a6c17c..7f1a1eccc4 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -247,6 +247,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount", "fieldname": "discount_and_margin", "fieldtype": "Section Break", "label": "Discount and Margin" @@ -876,7 +877,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-10-10 20:57:38.340026", + "modified": "2022-10-26 11:38:36.119339", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 82e92e87bc..b8203bd128 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -777,6 +777,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount", "fieldname": "discount_and_margin_section", "fieldtype": "Section Break", "label": "Discount and Margin" @@ -894,7 +895,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-09-07 11:12:38.634976", + "modified": "2022-10-26 16:47:41.364387", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 2cf836f9fc..ea0b25f41c 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -272,6 +272,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount", "fieldname": "discount_and_margin", "fieldtype": "Section Break", "label": "Discount and Margin" @@ -842,7 +843,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-09-06 13:24:18.065312", + "modified": "2022-10-26 16:05:02.712705", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 0a5cbabab0..77c3253432 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -261,6 +261,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount", "fieldname": "discount_and_margin", "fieldtype": "Section Break", "label": "Discount and Margin" @@ -814,7 +815,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-10-12 03:36:05.344847", + "modified": "2022-10-26 16:05:17.720768", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 772736e0ad..474ee92e26 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -919,6 +919,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount", "fieldname": "discount_and_margin_section", "fieldtype": "Section Break", "label": "Discount and Margin" @@ -1000,7 +1001,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-10-12 03:37:59.516609", + "modified": "2022-10-26 16:06:02.524435", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From 6063c4e3c0bca9ff6ad3798888b307c839025d5c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 26 Oct 2022 18:01:39 +0530 Subject: [PATCH 0414/1047] fix: Total Sales amount update in project via Sales Order --- erpnext/selling/doctype/sales_order/sales_order.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index f0e9e4b7d9..1f3419fd5d 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -627,6 +627,7 @@ def make_project(source_name, target_doc=None): "field_map": { "name": "sales_order", "base_grand_total": "estimated_costing", + "net_total": "total_sales_amount", }, }, }, From e7caa48e2fd2ad7bd8f2a45cbe25c79c7008a8a2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 26 Oct 2022 20:21:36 +0530 Subject: [PATCH 0415/1047] fix: Reference due date field type in Journal Entry Accounts table --- erpnext/accounts/doctype/journal_entry/journal_entry.js | 3 +-- erpnext/accounts/doctype/journal_entry/journal_entry.py | 6 +++++- .../journal_entry_account/journal_entry_account.json | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 763e2e6992..a5ff7f1aa7 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -312,8 +312,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro } } - get_outstanding(doctype, docname, company, child, due_date) { - var me = this; + get_outstanding(doctype, docname, company, child) { var args = { "doctype": doctype, "docname": docname, diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 52690e1e66..de012b28ec 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -1210,6 +1210,7 @@ def get_outstanding(args): args = json.loads(args) company_currency = erpnext.get_company_currency(args.get("company")) + due_date = None if args.get("doctype") == "Journal Entry": condition = " and party=%(party)s" if args.get("party") else "" @@ -1234,10 +1235,12 @@ def get_outstanding(args): invoice = frappe.db.get_value( args["doctype"], args["docname"], - ["outstanding_amount", "conversion_rate", scrub(party_type)], + ["outstanding_amount", "conversion_rate", scrub(party_type), "due_date"], as_dict=1, ) + due_date = invoice.get("due_date") + exchange_rate = ( invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1 ) @@ -1260,6 +1263,7 @@ def get_outstanding(args): "exchange_rate": exchange_rate, "party_type": party_type, "party": invoice.get(scrub(party_type)), + "reference_due_date": due_date, } diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index a0ea43332c..47ad19e0f9 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -216,7 +216,7 @@ { "depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])", "fieldname": "reference_due_date", - "fieldtype": "Select", + "fieldtype": "Date", "label": "Reference Due Date", "no_copy": 1 }, @@ -284,7 +284,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-10-13 17:07:17.999191", + "modified": "2022-10-26 20:03:10.906259", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", From a5a73ba85762e0940ea1c48f39b22f035923a860 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 26 Oct 2022 21:18:50 +0530 Subject: [PATCH 0416/1047] fix: Filter fixes in Accounts Payable report --- erpnext/accounts/report/accounts_payable/accounts_payable.js | 2 ++ .../accounts/report/accounts_receivable/accounts_receivable.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 7cf14e6738..e1a30a4b77 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -51,6 +51,8 @@ frappe.query_reports["Accounts Payable"] = { } else { frappe.query_report.set_filter_value('tax_id', ""); } + + frappe.query_report.refresh(); } }, { diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 8557c03bd5..f2ee1eb10e 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -748,7 +748,7 @@ class ReceivablePayableReport(object): self.add_accounting_dimensions_filters() - def get_cost_center_conditions(self, conditions): + def get_cost_center_conditions(self): lft, rgt = frappe.db.get_value("Cost Center", self.filters.cost_center, ["lft", "rgt"]) cost_center_list = [ center.name From 8f0e63cd27e05267905c0b5e0aa38bdc4393383f Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Wed, 26 Oct 2022 13:56:35 -0600 Subject: [PATCH 0417/1047] fix: add translate function on remark text on make_depreciation_entry in asset_value_adjustment.py --- .../doctype/asset_value_adjustment/asset_value_adjustment.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py index 59ab6a910d..84aa8fa023 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -61,7 +61,9 @@ class AssetValueAdjustment(Document): je.naming_series = depreciation_series je.posting_date = self.date je.company = self.company - je.remark = "Depreciation Entry against {0} worth {1}".format(self.asset, self.difference_amount) + je.remark = _("Depreciation Entry against {0} worth {1}").format( + self.asset, self.difference_amount + ) je.finance_book = self.finance_book credit_entry = { From de20dfe459210f5a28f26c7e22345f73a9462aa4 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 27 Oct 2022 09:33:49 +0530 Subject: [PATCH 0418/1047] chore: remove commented line --- .../process_statement_of_accounts.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index 48bc3a1bdd..c6b0c57ce5 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -242,8 +242,6 @@ def fetch_customers(customer_collection, collection_name, primary_mandatory): if int(primary_mandatory): if primary_email == "": continue - # elif (billing_email == "") and (primary_email == ""): - # continue customer_list.append( {"name": customer.name, "primary_email": primary_email, "billing_email": billing_email} From a04c44fe34845b6e1e254fc86ea452a6ce8f3930 Mon Sep 17 00:00:00 2001 From: Vishal Date: Fri, 28 Oct 2022 11:36:14 +0530 Subject: [PATCH 0419/1047] chore: Added Material Request Reference in Purchase Recipt Dashboard for Tracking --- .../doctype/purchase_receipt/purchase_receipt_dashboard.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py index 06ba936556..60e5fcffd0 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py @@ -1,6 +1,5 @@ from frappe import _ - def get_data(): return { "fieldname": "purchase_receipt_no", @@ -12,13 +11,14 @@ def get_data(): "Purchase Receipt": "return_against", }, "internal_links": { + "Material Request": ["items", "material_request"], "Purchase Order": ["items", "purchase_order"], "Project": ["items", "project"], "Quality Inspection": ["items", "quality_inspection"], }, "transactions": [ {"label": _("Related"), "items": ["Purchase Invoice", "Landed Cost Voucher", "Asset"]}, - {"label": _("Reference"), "items": ["Purchase Order", "Quality Inspection", "Project"]}, + {"label": _("Reference"), "items": ["Material Request", "Purchase Order", "Quality Inspection", "Project"]}, {"label": _("Returns"), "items": ["Purchase Receipt"]}, {"label": _("Subscription"), "items": ["Auto Repeat"]}, ], From e8c01570176212322bf7b0688ece2f3fbf98a184 Mon Sep 17 00:00:00 2001 From: Vishal Date: Fri, 28 Oct 2022 11:59:10 +0530 Subject: [PATCH 0420/1047] chore: minor linting issue fixed --- .../doctype/purchase_receipt/purchase_receipt_dashboard.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py index 60e5fcffd0..b3ae7b58b4 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py @@ -1,5 +1,6 @@ from frappe import _ + def get_data(): return { "fieldname": "purchase_receipt_no", @@ -18,7 +19,10 @@ def get_data(): }, "transactions": [ {"label": _("Related"), "items": ["Purchase Invoice", "Landed Cost Voucher", "Asset"]}, - {"label": _("Reference"), "items": ["Material Request", "Purchase Order", "Quality Inspection", "Project"]}, + { + "label": _("Reference"), + "items": ["Material Request", "Purchase Order", "Quality Inspection", "Project"], + }, {"label": _("Returns"), "items": ["Purchase Receipt"]}, {"label": _("Subscription"), "items": ["Auto Repeat"]}, ], From aadb6b1772815095a1778ce324e6877b6464d55a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 28 Oct 2022 16:22:52 +0530 Subject: [PATCH 0421/1047] feat: additional filters on Payment terms report Filter on Status and Due dates --- .../payment_terms_status_for_sales_order.js | 30 ++++++++++++++++++- .../payment_terms_status_for_sales_order.py | 14 +++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js index c068ae3b5a..991ac719cd 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js @@ -74,7 +74,35 @@ function get_filters() { ] } } - } + }, + { + "fieldname":"from_due_date", + "label": __("From Due Date"), + "fieldtype": "Date", + }, + { + "fieldname":"to_due_date", + "label": __("To Due Date"), + "fieldtype": "Date", + }, + { + "fieldname":"status", + "label": __("Status"), + "fieldtype": "MultiSelectList", + "width": 100, + get_data: function(txt) { + let status = ["Overdue", "Unpaid", "Completed", "Partly Paid"] + let options = [] + for (let option of status){ + options.push({ + "value": option, + "label": __(option), + "description": "" + }) + } + return options + } + }, ] return filters; } diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py index 91f4a5e50a..8bf56865a7 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py @@ -162,6 +162,12 @@ def build_filter_criterions(filters): if filters.item: qb_criterions.append(qb.DocType("Sales Order Item").item_code == filters.item) + if filters.from_due_date: + qb_criterions.append(qb.DocType("Payment Schedule").due_date.gte(filters.from_due_date)) + + if filters.to_due_date: + qb_criterions.append(qb.DocType("Payment Schedule").due_date.lte(filters.to_due_date)) + return qb_criterions @@ -279,11 +285,19 @@ def prepare_chart(s_orders): return chart +def filter_on_calculated_status(filters, sales_orders): + if filters.status and sales_orders: + return [x for x in sales_orders if x.status in filters.status] + return sales_orders + + def execute(filters=None): columns = get_columns() sales_orders, so_invoices = get_so_with_invoices(filters) sales_orders, so_invoices = set_payment_terms_statuses(sales_orders, so_invoices, filters) + sales_orders = filter_on_calculated_status(filters, sales_orders) + prepare_chart(sales_orders) data = sales_orders From 4765f937eaa0dd6cf3397245a5ef55d27805df16 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 28 Oct 2022 17:50:01 +0530 Subject: [PATCH 0422/1047] fix: key error in filter access --- ...st_payment_terms_status_for_sales_order.py | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py index 9d542f5079..67c96af901 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py @@ -77,12 +77,14 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase): sinv.insert() sinv.submit() columns, data, message, chart = execute( - { - "company": "_Test Company", - "period_start_date": "2021-06-01", - "period_end_date": "2021-06-30", - "item": item.item_code, - } + frappe._dict( + { + "company": "_Test Company", + "period_start_date": "2021-06-01", + "period_end_date": "2021-06-30", + "item": item.item_code, + } + ) ) expected_value = [ @@ -167,12 +169,14 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase): sinv.insert() sinv.submit() columns, data, message, chart = execute( - { - "company": "_Test Company", - "period_start_date": "2021-06-01", - "period_end_date": "2021-06-30", - "item": item.item_code, - } + frappe._dict( + { + "company": "_Test Company", + "period_start_date": "2021-06-01", + "period_end_date": "2021-06-30", + "item": item.item_code, + } + ) ) # report defaults to company currency. From fed39a53cbbae7743b5aa65bba3f6f93044e751e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 28 Oct 2022 18:14:51 +0530 Subject: [PATCH 0423/1047] test: due date filter on Payment Terms report --- ...st_payment_terms_status_for_sales_order.py | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py index 67c96af901..525ae8e7ea 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py @@ -2,7 +2,7 @@ import datetime import frappe from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days +from frappe.utils import add_days, nowdate from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order @@ -342,3 +342,60 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase): with self.subTest(filters=filters): columns, data, message, chart = execute(filters) self.assertEqual(data, expected_values_for_group_filters[idx]) + + def test_04_due_date_filter(self): + self.create_payment_terms_template() + item = create_item(item_code="_Test Excavator 1", is_stock_item=0) + transaction_date = nowdate() + so = make_sales_order( + transaction_date=add_days(transaction_date, -30), + delivery_date=add_days(transaction_date, -15), + item=item.item_code, + qty=10, + rate=100000, + do_not_save=True, + ) + so.po_no = "" + so.taxes_and_charges = "" + so.taxes = "" + so.payment_terms_template = self.template.name + so.save() + so.submit() + + # make invoice with 60% of the total sales order value + sinv = make_sales_invoice(so.name) + sinv.taxes_and_charges = "" + sinv.taxes = "" + sinv.items[0].qty = 6 + sinv.insert() + sinv.submit() + columns, data, message, chart = execute( + frappe._dict( + { + "company": "_Test Company", + "item": item.item_code, + "from_due_date": add_days(transaction_date, -30), + "to_due_date": add_days(transaction_date, -15), + } + ) + ) + + expected_value = [ + { + "name": so.name, + "customer": so.customer, + "submitted": datetime.date.fromisoformat(add_days(transaction_date, -30)), + "status": "Completed", + "payment_term": None, + "description": "_Test 50-50", + "due_date": datetime.date.fromisoformat(add_days(transaction_date, -15)), + "invoice_portion": 50.0, + "currency": "INR", + "base_payment_amount": 500000.0, + "paid_amount": 500000.0, + "invoices": "," + sinv.name, + }, + ] + # Only the first term should be pulled + self.assertEqual(len(data), 1) + self.assertEqual(data, expected_value) From b9d497c61cc0db8fa1acdbb43672396db8eeb983 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 28 Oct 2022 20:18:55 +0530 Subject: [PATCH 0424/1047] chore: Update patch --- erpnext/patches/v14_0/update_tds_fields.py | 49 ++++++++++------------ 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/erpnext/patches/v14_0/update_tds_fields.py b/erpnext/patches/v14_0/update_tds_fields.py index 98feee2ac7..513acb2523 100644 --- a/erpnext/patches/v14_0/update_tds_fields.py +++ b/erpnext/patches/v14_0/update_tds_fields.py @@ -1,30 +1,25 @@ import frappe -def execute(): - frappe.db.sql( - """ - UPDATE - `tabPurchase Invoice Item` - INNER JOIN - `tabPurchase Invoice` - ON - `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.parent - SET - `tabPurchase Invoice Item`.apply_tds = 1 - WHERE - `tabPurchase Invoice`.apply_tds = 1 - and `tabPurchase Invoice`.docstatus = 1 - """ - ) +from erpnext.accounts.utils import get_fiscal_year - frappe.db.sql( - """ - UPDATE - `tabPurchase Invoice` - SET - tax_withholding_net_total = net_total, - base_tax_withholding_net_total = base_net_total - WHERE - apply_tds = 1 and docstatus = 1 - """ - ) + +def execute(): + # Only do for current fiscal year, no need to repost for all years + for company in frappe.get_all("Company"): + fiscal_year_details = get_fiscal_year(company=company.name, as_dict=True) + + purchase_invoice = frappe.qb.DocType("Purchase Invoice") + + frappe.qb.update(purchase_invoice).set( + purchase_invoice.tax_withholding_net_total, purchase_invoice.net_total + ).set( + purchase_invoice.base_tax_withholding_net_total, purchase_invoice.base_net_total + ).where( + purchase_invoice.company == company.name + ).where( + purchase_invoice.apply_tds == 1 + ).where( + purchase_invoice.posting_date >= fiscal_year_details.year_start_date + ).where( + purchase_invoice.docstatus == 1 + ).run() From d9eda45b0b37366ff746e1f4b35c6426d964931e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 28 Oct 2022 20:19:13 +0530 Subject: [PATCH 0425/1047] chore: Minor updates --- .../tax_withholding_category/tax_withholding_category.py | 4 +++- erpnext/public/js/controllers/transaction.js | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index b4f46cca30..03d5c680c2 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -291,7 +291,9 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): {"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")} ) - invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", "base_net_total"]) + invoices_details = frappe.get_all( + doctype, filters=filters, fields=["name", "base_tax_withholding_net_total as base_net_total"] + ) for d in invoices_details: vouchers.append(d.name) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 7fecb18fad..ec251994d0 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1200,7 +1200,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe "base_rounding_adjustment"], company_currency); this.frm.set_currency_labels(["total", "net_total", "total_taxes_and_charges", "discount_amount", - "grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted", + "grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted", "tax_withholding_net_total", "rounded_total", "in_words", "paid_amount", "write_off_amount", "operating_cost", "scrap_material_cost", "rounding_adjustment", "raw_material_cost", "total_cost"], this.frm.doc.currency); @@ -1217,7 +1217,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } // toggle fields - this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total", + this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total", "base_tax_withholding_net_total", "base_total_taxes_and_charges", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", "base_grand_total", "base_rounded_total", "base_in_words", "base_discount_amount", "base_paid_amount", "base_write_off_amount", "base_operating_cost", "base_raw_material_cost", From 49343e9f6823c5214bc8fd8d1a6fdd8cb2d72a8d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 28 Oct 2022 22:03:09 +0530 Subject: [PATCH 0426/1047] chore: column name --- .../tax_withholding_category/tax_withholding_category.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 03d5c680c2..30ed91b974 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -275,6 +275,11 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice" + field = ( + "base_tax_withholding_net_total as base_net_total" + if party_type == "Supplier" + else "base_net_total" + ) voucher_wise_amount = {} vouchers = [] @@ -291,9 +296,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): {"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")} ) - invoices_details = frappe.get_all( - doctype, filters=filters, fields=["name", "base_tax_withholding_net_total as base_net_total"] - ) + invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", field]) for d in invoices_details: vouchers.append(d.name) From 15ebf4a0cf87360ce4265014fe23b2a95e171506 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Sat, 29 Oct 2022 11:56:34 +0530 Subject: [PATCH 0427/1047] fix: add `Sales Order` reference in Material Request Dashboard --- .../doctype/material_request/material_request_dashboard.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/material_request_dashboard.py b/erpnext/stock/doctype/material_request/material_request_dashboard.py index b073e6a22e..691a8b39b1 100644 --- a/erpnext/stock/doctype/material_request/material_request_dashboard.py +++ b/erpnext/stock/doctype/material_request/material_request_dashboard.py @@ -4,10 +4,13 @@ from frappe import _ def get_data(): return { "fieldname": "material_request", + "internal_links": { + "Sales Order": ["items", "sales_order"], + }, "transactions": [ { "label": _("Reference"), - "items": ["Request for Quotation", "Supplier Quotation", "Purchase Order"], + "items": ["Sales Order", "Request for Quotation", "Supplier Quotation", "Purchase Order"], }, {"label": _("Stock"), "items": ["Stock Entry", "Purchase Receipt", "Pick List"]}, {"label": _("Manufacturing"), "items": ["Work Order"]}, From 54c2ffc36b8976fdc0139a8cb06e5e0a9d4f19e5 Mon Sep 17 00:00:00 2001 From: Hossein Yousefian <86075967+ihosseinu@users.noreply.github.com> Date: Sat, 29 Oct 2022 20:24:59 +0330 Subject: [PATCH 0428/1047] fix: Pass project to stock entry items fix: Pass project to stock entry items --- erpnext/assets/doctype/asset_repair/asset_repair.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 8758e9c17d..d5913c5946 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -135,6 +135,7 @@ class AssetRepair(AccountsController): "basic_rate": stock_item.valuation_rate, "serial_no": stock_item.serial_no, "cost_center": self.cost_center, + "project": self.project, }, ) From 4e26d42d1723fcc82ed8f990fad4bf0f4a56d135 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 30 Oct 2022 19:33:27 +0530 Subject: [PATCH 0429/1047] fix: Budget validation for main cost center --- erpnext/accounts/doctype/budget/budget.py | 8 ++--- .../accounts/doctype/budget/test_budget.py | 33 +++++++++++++++++++ erpnext/accounts/general_ledger.py | 6 ++++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py index 6ac3350c3b..637ac7a04c 100644 --- a/erpnext/accounts/doctype/budget/budget.py +++ b/erpnext/accounts/doctype/budget/budget.py @@ -107,7 +107,7 @@ class Budget(Document): self.naming_series = f"{{{frappe.scrub(self.budget_against)}}}./.{self.fiscal_year}/.###" -def validate_expense_against_budget(args): +def validate_expense_against_budget(args, expense_amount=0): args = frappe._dict(args) if args.get("company") and not args.fiscal_year: @@ -175,13 +175,13 @@ def validate_expense_against_budget(args): ) # nosec if budget_records: - validate_budget_records(args, budget_records) + validate_budget_records(args, budget_records, expense_amount) -def validate_budget_records(args, budget_records): +def validate_budget_records(args, budget_records, expense_amount): for budget in budget_records: if flt(budget.budget_amount): - amount = get_amount(args, budget) + amount = expense_amount or get_amount(args, budget) yearly_action, monthly_action = get_actions(args, budget) if monthly_action in ["Stop", "Warn"]: diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py index c48c7d97a2..11af9a29f6 100644 --- a/erpnext/accounts/doctype/budget/test_budget.py +++ b/erpnext/accounts/doctype/budget/test_budget.py @@ -334,6 +334,39 @@ class TestBudget(unittest.TestCase): budget.cancel() jv.cancel() + def test_monthly_budget_against_main_cost_center(self): + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + from erpnext.accounts.doctype.cost_center_allocation.test_cost_center_allocation import ( + create_cost_center_allocation, + ) + + cost_centers = [ + "Main Budget Cost Center 1", + "Sub Budget Cost Center 1", + "Sub Budget Cost Center 2", + ] + + for cc in cost_centers: + create_cost_center(cost_center_name=cc, company="_Test Company") + + create_cost_center_allocation( + "_Test Company", + "Main Budget Cost Center 1 - _TC", + {"Sub Budget Cost Center 1 - _TC": 60, "Sub Budget Cost Center 2 - _TC": 40}, + ) + + make_budget(budget_against="Cost Center", cost_center="Main Budget Cost Center 1 - _TC") + + jv = make_journal_entry( + "_Test Account Cost for Goods Sold - _TC", + "_Test Bank - _TC", + 400000, + "Main Budget Cost Center 1 - _TC", + posting_date=nowdate(), + ) + + self.assertRaises(BudgetError, jv.submit) + def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None): if budget_against_field == "project": diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index f4a50a5f91..6d164eef2b 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -128,6 +128,12 @@ def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None): new_gl_map = [] for d in gl_map: cost_center = d.get("cost_center") + + # Validate budget against main cost center + validate_expense_against_budget( + d, expense_amount=flt(d.debit, precision) - flt(d.credit, precision) + ) + if cost_center and cost_center_allocation.get(cost_center): for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items(): gle = copy.deepcopy(d) From a612a666f3cfe4f9b658786cb108c154d85e9623 Mon Sep 17 00:00:00 2001 From: gn306029 Date: Mon, 31 Oct 2022 12:32:57 +0800 Subject: [PATCH 0430/1047] chore: Update zh-TW translations (#31775) --- erpnext/translations/zh-TW.csv | 428 ++++++++++++++++----------------- 1 file changed, 214 insertions(+), 214 deletions(-) diff --git a/erpnext/translations/zh-TW.csv b/erpnext/translations/zh-TW.csv index de1d7632e7..c30dd7231c 100644 --- a/erpnext/translations/zh-TW.csv +++ b/erpnext/translations/zh-TW.csv @@ -10,20 +10,20 @@ DocType: Supplier Scorecard,Notify Supplier,通知供應商 apps/erpnext/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js +52,Please select Party Type first,請選擇黨第一型 DocType: Item,Customer Items,客戶項目 DocType: Project,Costing and Billing,成本核算和計費 -apps/erpnext/erpnext/hr/doctype/employee_advance/employee_advance.py +43,Advance account currency should be same as company currency {0},預付帳戶貨幣應與公司貨幣{0}相同 +apps/erpnext/erpnext/hr/doctype/employee_advance/employee_advance.py +43,Advance account currency should be same as company currency {0},預付科目貨幣應與公司貨幣{0}相同 DocType: QuickBooks Migrator,Token Endpoint,令牌端點 -apps/erpnext/erpnext/accounts/doctype/account/account.py +55,Account {0}: Parent account {1} can not be a ledger,帳戶{0}:父帳戶{1}不能是總帳 +apps/erpnext/erpnext/accounts/doctype/account/account.py +55,Account {0}: Parent account {1} can not be a ledger,科目{0}:上層科目{1}不能是總帳 DocType: Item,Publish Item to hub.erpnext.com,發布項目hub.erpnext.com apps/erpnext/erpnext/hr/doctype/leave_application/leave_application.py +270,Cannot find active Leave Period,找不到有效的休假期 apps/erpnext/erpnext/hr/doctype/employee/employee_dashboard.py +22,Evaluation,評估 DocType: Item,Default Unit of Measure,預設的計量單位 DocType: SMS Center,All Sales Partner Contact,所有的銷售合作夥伴聯絡 DocType: Department,Leave Approvers,休假審批人 -DocType: Employee,Bio / Cover Letter,生物/求職信 +DocType: Employee,Bio / Cover Letter,自傳/求職信 DocType: Patient Encounter,Investigations,調查 DocType: Restaurant Order Entry,Click Enter To Add,點擊輸入要添加 apps/erpnext/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +29,"Missing value for Password, API Key or Shopify URL",缺少密碼,API密鑰或Shopify網址的值 -apps/erpnext/erpnext/public/js/setup_wizard.js +243,All Accounts,所有帳戶 +apps/erpnext/erpnext/public/js/setup_wizard.js +243,All Accounts,所有科目 apps/erpnext/erpnext/hr/doctype/employee_transfer/employee_transfer.py +15,Cannot transfer Employee with status Left,無法轉移狀態為左的員工 apps/erpnext/erpnext/manufacturing/doctype/production_order/production_order.py +216,"Stopped Production Order cannot be cancelled, Unstop it first to cancel",停止生產訂單無法取消,首先Unstop它取消 apps/erpnext/erpnext/assets/doctype/asset/asset.js +338,Do you really want to scrap this asset?,難道你真的想放棄這項資產? @@ -48,12 +48,12 @@ DocType: Allowed To Transact With,Allowed To Transact With,允許與 DocType: Bank Guarantee,Customer,客戶 DocType: Purchase Receipt Item,Required By,需求來自 DocType: Delivery Note,Return Against Delivery Note,射向送貨單 -DocType: Asset Category,Finance Book Detail,財務圖書細節 +DocType: Asset Category,Finance Book Detail,財務帳簿細節 DocType: Purchase Order,% Billed,%已開立帳單 apps/erpnext/erpnext/controllers/sales_and_purchase_return.py +41,Exchange Rate must be same as {0} {1} ({2}),匯率必須一致{0} {1}({2}) DocType: Sales Invoice,Customer Name,客戶名稱 DocType: Vehicle,Natural Gas,天然氣 -apps/erpnext/erpnext/setup/setup_wizard/operations/company_setup.py +63,Bank account cannot be named as {0},銀行賬戶不能命名為{0} +apps/erpnext/erpnext/setup/setup_wizard/operations/company_setup.py +63,Bank account cannot be named as {0},銀行科目不能命名為{0} DocType: Employee Tax Exemption Declaration,HRA as per Salary Structure,HRA根據薪資結構 DocType: Account,Heads (or groups) against which Accounting Entries are made and balances are maintained.,頭(或組)針對其會計分錄是由和平衡得以維持。 apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +197,Outstanding for {0} cannot be less than zero ({1}),傑出的{0}不能小於零( {1} ) @@ -142,11 +142,11 @@ apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan_dashb apps/erpnext/erpnext/hr/doctype/attendance/attendance.py +46,Attendance date can not be less than employee's joining date,考勤日期不得少於員工的加盟日期 DocType: Grading Scale,Grading Scale Name,分級標準名稱 apps/erpnext/erpnext/public/js/hub/marketplace.js +147,Add Users to Marketplace,將用戶添加到市場 -apps/erpnext/erpnext/accounts/doctype/account/account.js +37,This is a root account and cannot be edited.,這是一個 root 帳戶,不能被編輯。 -DocType: BOM,Operations,作業 +apps/erpnext/erpnext/accounts/doctype/account/account.js +37,This is a root account and cannot be edited.,這是一個 root 科目,不能被編輯。 +DocType: BOM,Operations,操作 apps/erpnext/erpnext/setup/doctype/authorization_rule/authorization_rule.py +38,Cannot set authorization on basis of Discount for {0},不能在折扣的基礎上設置授權{0} DocType: Subscription,Subscription Start Date,訂閱開始日期 -DocType: Healthcare Settings,Default receivable accounts to be used if not set in Patient to book Appointment charges.,如果未在患者中設置預約費用,則使用默認應收帳戶。 +DocType: Healthcare Settings,Default receivable accounts to be used if not set in Patient to book Appointment charges.,如果未在患者中設置預約費用,則使用默認應收科目。 DocType: Rename Tool,"Attach .csv file with two columns, one for the old name and one for the new name",附加.csv文件有兩列,一為舊名稱,一個用於新名稱 apps/erpnext/erpnext/regional/report/eway_bill/eway_bill.py +195,From Address 2,來自地址2 apps/erpnext/erpnext/accounts/utils.py +74,{0} {1} not in any active Fiscal Year.,{0} {1} 不在任何有效的會計年度 @@ -216,14 +216,14 @@ apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py +36,From {0} to {1 apps/erpnext/erpnext/setup/setup_wizard/setup_wizard.py +51,Failed to setup taxes,無法設置稅收 DocType: Item,Copy From Item Group,從項目群組複製 DocType: Journal Entry,Opening Entry,開放報名 -apps/erpnext/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js +25,Account Pay Only,賬戶只需支付 +apps/erpnext/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js +25,Account Pay Only,科目只需支付 DocType: Loan,Repay Over Number of Periods,償還期的超過數 DocType: Stock Entry,Additional Costs,額外費用 -apps/erpnext/erpnext/accounts/doctype/account/account.py +145,Account with existing transaction can not be converted to group.,帳戶與現有的交易不能被轉換到群組。 +apps/erpnext/erpnext/accounts/doctype/account/account.py +145,Account with existing transaction can not be converted to group.,科目與現有的交易不能被轉換到群組。 DocType: Lead,Product Enquiry,產品查詢 DocType: Education Settings,Validate Batch for Students in Student Group,驗證學生組學生的批次 apps/erpnext/erpnext/hr/doctype/attendance/attendance.py +38,No leave record found for employee {0} for {1},未找到員工的假期記錄{0} {1} -DocType: Company,Unrealized Exchange Gain/Loss Account,未實現的匯兌收益/損失賬戶 +DocType: Company,Unrealized Exchange Gain/Loss Account,未實現的匯兌收益/損失科目 apps/erpnext/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js +23,Please enter company first,請先輸入公司 apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.js +626,Please select Company first,請首先選擇公司 DocType: Employee Education,Under Graduate,根據研究生 @@ -236,7 +236,7 @@ DocType: Fee Schedule,Send Payment Request Email,發送付款請求電子郵件 apps/erpnext/erpnext/manufacturing/doctype/bom/bom.py +282,Item {0} does not exist in the system or has expired,項目{0}不存在於系統中或已過期 DocType: Supplier,Leave blank if the Supplier is blocked indefinitely,如果供應商被無限期封鎖,請留空 apps/erpnext/erpnext/setup/setup_wizard/data/industry_type.py +44,Real Estate,房地產 -apps/erpnext/erpnext/accounts/report/general_ledger/general_ledger.html +1,Statement of Account,帳戶狀態 +apps/erpnext/erpnext/accounts/report/general_ledger/general_ledger.html +1,Statement of Account,科目狀態 apps/erpnext/erpnext/setup/setup_wizard/data/industry_type.py +41,Pharmaceuticals,製藥 DocType: Purchase Invoice Item,Is Fixed Asset,是固定的資產 apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py +355,"Available qty is {0}, you need {1}",可用數量是{0},則需要{1} @@ -284,7 +284,7 @@ DocType: Production Plan,Material Request Detail,材料請求詳情 DocType: Selling Settings,Default Quotation Validity Days,默認報價有效天數 apps/erpnext/erpnext/controllers/accounts_controller.py +886,"To include tax in row {0} in Item rate, taxes in rows {1} must also be included",要包括稅款,行{0}項率,稅收行{1}也必須包括在內 DocType: Payroll Entry,Validate Attendance,驗證出席 -DocType: Sales Invoice,Change Amount,漲跌額 +DocType: Sales Invoice,Change Amount,變動金額 DocType: Party Tax Withholding Config,Certificate Received,已收到證書 DocType: GST Settings,Set Invoice Value for B2C. B2CL and B2CS calculated based on this invoice value.,設置B2C的發票值。 B2CL和B2CS根據此發票值計算。 DocType: BOM Update Tool,New BOM,新的物料清單 @@ -294,7 +294,7 @@ DocType: Supplier Group,Supplier Group Name,供應商集團名稱 DocType: Driver,Driving License Categories,駕駛執照類別 apps/erpnext/erpnext/selling/doctype/sales_order/sales_order.py +124,Please enter Delivery Date,請輸入交貨日期 DocType: Depreciation Schedule,Make Depreciation Entry,計提折舊進入 -DocType: Closed Document,Closed Document,封閉文件 +DocType: Closed Document,Closed Document,關閉文件 DocType: HR Settings,Leave Settings,保留設置 apps/erpnext/erpnext/hr/doctype/staffing_plan/staffing_plan.js +76,Number of positions cannot be less then current count of employees,職位數量不能少於當前員工人數 DocType: Lead,Request Type,請求類型 @@ -308,7 +308,7 @@ apps/erpnext/erpnext/setup/setup_wizard/operations/install_fixtures.py +144,Exec apps/erpnext/erpnext/config/manufacturing.py +62,Details of the operations carried out.,進行的作業細節。 DocType: Asset Maintenance Log,Maintenance Status,維修狀態 apps/erpnext/erpnext/non_profit/doctype/member/member_dashboard.py +10,Membership Details,會員資格 -apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +56,{0} {1}: Supplier is required against Payable account {2},{0} {1}:需要對供應商應付賬款{2} +apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +56,{0} {1}: Supplier is required against Payable account {2},{0} {1}:需要對供應商應付帳款{2} apps/erpnext/erpnext/config/selling.py +52,Items and Pricing,項目和定價 apps/erpnext/erpnext/projects/doctype/project/project_dashboard.html +2,Total hours: {0},總時間:{0} apps/erpnext/erpnext/accounts/report/trial_balance/trial_balance.py +43,From Date should be within the Fiscal Year. Assuming From Date = {0},從日期應該是在財政年度內。假設起始日期={0} @@ -369,12 +369,12 @@ DocType: Company,Default Payroll Payable Account,默認情況下,應付職工 apps/erpnext/erpnext/education/doctype/student_group/student_group.js +51,Update Email Group,更新電子郵件組 DocType: Sales Invoice,Is Opening Entry,是開放登錄 DocType: Lab Test Template,"If unchecked, the item wont be appear in Sales Invoice, but can be used in group test creation. ",如果取消選中,該項目不會出現在銷售發票中,但可用於創建組測試。 -DocType: Customer Group,Mention if non-standard receivable account applicable,何況,如果不規範應收賬款適用 +DocType: Customer Group,Mention if non-standard receivable account applicable,何況,如果不規範應收帳款適用 DocType: Course Schedule,Instructor Name,導師姓名 DocType: Company,Arrear Component,欠費組件 DocType: Supplier Scorecard,Criteria Setup,條件設置 apps/erpnext/erpnext/manufacturing/doctype/production_order/production_order.py +199,For Warehouse is required before Submit,對於倉庫之前,需要提交 -DocType: Codification Table,Medical Code,醫療法 +DocType: Codification Table,Medical Code,醫療代號 apps/erpnext/erpnext/config/integrations.py +37,Connect Amazon with ERPNext,將Amazon與ERPNext連接起來 apps/erpnext/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py +20,Please enter Company,請輸入公司名稱 DocType: Delivery Note Item,Against Sales Invoice Item,對銷售發票項目 @@ -406,9 +406,9 @@ DocType: Inpatient Record,Discharge Scheduled,出院預定 apps/erpnext/erpnext/hr/doctype/employee/employee.py +138,Relieving Date must be greater than Date of Joining,解除日期必須大於加入的日期 DocType: POS Closing Voucher,Cashier,出納員 apps/erpnext/erpnext/setup/setup_wizard/operations/install_fixtures.py +198,Leaves per Year,每年葉 -apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +162,Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.,行{0}:請檢查'是推進'對帳戶{1},如果這是一個進步條目。 +apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +162,Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.,行{0}:請檢查'是進階'對科目{1},如果這是一個進階條目。 apps/erpnext/erpnext/stock/utils.py +243,Warehouse {0} does not belong to company {1},倉庫{0}不屬於公司{1} -DocType: Email Digest,Profit & Loss,利潤損失 +DocType: Email Digest,Profit & Loss,收益與損失 DocType: Task,Total Costing Amount (via Time Sheet),總成本計算量(通過時間表) apps/erpnext/erpnext/education/doctype/fee_schedule/fee_schedule.py +76,Please setup Students under Student Groups,請設置學生組的學生 DocType: Item Website Specification,Item Website Specification,項目網站規格 @@ -479,7 +479,7 @@ apps/erpnext/erpnext/config/desktop.py +159,Learn,學習 DocType: Purchase Invoice Item,Enable Deferred Expense,啟用延期費用 DocType: Asset,Next Depreciation Date,接下來折舊日期 apps/erpnext/erpnext/projects/doctype/activity_type/activity_type.js +3,Activity Cost per Employee,每個員工活動費用 -DocType: Accounts Settings,Settings for Accounts,設置帳戶 +DocType: Accounts Settings,Settings for Accounts,會計設定 apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +800,Supplier Invoice No exists in Purchase Invoice {0},供應商發票不存在採購發票{0} apps/erpnext/erpnext/config/selling.py +118,Manage Sales Person Tree.,管理銷售人員樹。 apps/erpnext/erpnext/stock/doctype/delivery_trip/delivery_trip.py +105,"Cannot process route, since Google Maps Settings is disabled.",由於禁用了Google地圖設置,因此無法處理路線。 @@ -521,7 +521,7 @@ apps/erpnext/erpnext/setup/doctype/email_digest/templates/default.html +97,Upcom apps/erpnext/erpnext/public/js/templates/item_quick_entry.html +1,Variant Attributes,變量屬性 apps/erpnext/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +114,Please select month and year,請選擇年份和月份 DocType: Employee,Company Email,企業郵箱 -DocType: GL Entry,Debit Amount in Account Currency,在賬戶幣種借記金額 +DocType: GL Entry,Debit Amount in Account Currency,在科目幣種借記金額 apps/erpnext/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py +21,Order Value,訂單價值 apps/erpnext/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py +21,Order Value,訂單價值 DocType: Certified Consultant,Certified Consultant,認證顧問 @@ -550,7 +550,7 @@ apps/erpnext/erpnext/accounts/doctype/cost_center/cost_center.js +101,Convert to DocType: Project Update,Good/Steady,好/穩定 DocType: Bank Statement Transaction Invoice Item,Invoice Date,發票日期 DocType: GL Entry,Debit Amount,借方金額 -apps/erpnext/erpnext/accounts/party.py +277,There can only be 1 Account per Company in {0} {1},只能有每公司1帳戶{0} {1} +apps/erpnext/erpnext/accounts/party.py +277,There can only be 1 Account per Company in {0} {1},只能有每公司1科目{0} {1} DocType: Support Search Source,Response Result Key Path,響應結果關鍵路徑 DocType: Journal Entry,Inter Company Journal Entry,Inter公司日記帳分錄 apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py +534,For quantity {0} should not be grater than work order quantity {1},數量{0}不應超過工單數量{1} @@ -562,7 +562,7 @@ apps/erpnext/erpnext/accounts/report/accounts_receivable/accounts_receivable.htm DocType: Setup Progress Action,Action Document,行動文件 DocType: Chapter Member,Website URL,網站網址 DocType: Delivery Note,Instructions,說明 -DocType: Quality Inspection,Inspected By,視察 +DocType: Quality Inspection,Inspected By,檢查 DocType: Asset Maintenance Log,Maintenance Type,維護類型 apps/erpnext/erpnext/education/doctype/student_group/student_group.py +45,{0} - {1} is not enrolled in the Course {2},{0} - {1} 未在課程中註冊 {2} apps/erpnext/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.html +225,Student Name: ,學生姓名: @@ -642,17 +642,17 @@ apps/erpnext/erpnext/stock/doctype/packing_slip/packing_slip.js +57,'To Case No. DocType: Certification Application,Non Profit,非營利 DocType: Production Plan,Not Started,未啟動 DocType: Lead,Channel Partner,渠道合作夥伴 -DocType: Account,Old Parent,老家長 +DocType: Account,Old Parent,舊上級 apps/erpnext/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py +24,Mandatory field - Academic Year,必修課 - 學年 apps/erpnext/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py +24,Mandatory field - Academic Year,必修課 - 學年 apps/erpnext/erpnext/accounts/doctype/payment_entry/payment_entry.py +221,{0} {1} is not associated with {2} {3},{0} {1} 未與 {2} {3} 關聯 DocType: Notification Control,Customize the introductory text that goes as a part of that email. Each transaction has a separate introductory text.,自定義去作為郵件的一部分的介紹文字。每筆交易都有一個單獨的介紹性文字。 apps/erpnext/erpnext/manufacturing/doctype/job_card/job_card.py +56,Row {0} : Operation is required against the raw material item {1},行{0}:對原材料項{1}需要操作 -apps/erpnext/erpnext/hr/doctype/expense_claim/expense_claim.py +180,Please set default payable account for the company {0},請為公司{0}設置預設應付賬款 +apps/erpnext/erpnext/hr/doctype/expense_claim/expense_claim.py +180,Please set default payable account for the company {0},請為公司{0}設置預設應付帳款 apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py +608,Transaction not allowed against stopped Work Order {0},不允許對停止的工單{0}進行交易 DocType: Setup Progress Action,Min Doc Count,最小文件計數 apps/erpnext/erpnext/config/manufacturing.py +84,Global settings for all manufacturing processes.,所有製造過程中的全域設定。 -DocType: Accounts Settings,Accounts Frozen Upto,帳戶被凍結到 +DocType: Accounts Settings,Accounts Frozen Upto,科目被凍結到 DocType: SMS Log,Sent On,發送於 apps/erpnext/erpnext/stock/doctype/item/item.py +778,Attribute {0} selected multiple times in Attributes Table,屬性{0}多次選擇在屬性表 DocType: HR Settings,Employee record is created using selected field. ,使用所選欄位創建員工記錄。 @@ -717,7 +717,7 @@ apps/erpnext/erpnext/education/doctype/student_group/student_group.py +22,Please DocType: Codification Table,Codification Table,編纂表 DocType: Timesheet Detail,Hrs,小時 apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.js +410,Please select Company,請選擇公司 -DocType: Stock Entry Detail,Difference Account,差異帳戶 +DocType: Stock Entry Detail,Difference Account,差異科目 DocType: Purchase Invoice,Supplier GSTIN,供應商GSTIN apps/erpnext/erpnext/projects/doctype/task/task.py +50,Cannot close task as its dependant task {0} is not closed.,不能因為其依賴的任務{0}沒有關閉關閉任務。 apps/erpnext/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py +435,Please enter Warehouse for which Material Request will be raised,請輸入物料需求欲增加的倉庫 @@ -759,7 +759,7 @@ apps/erpnext/erpnext/config/stock.py +337,Managing Subcontracting,管理轉包 DocType: Vital Signs,Body Temperature,體溫 DocType: Project,Project will be accessible on the website to these users,項目將在網站向這些用戶上訪問 apps/erpnext/erpnext/stock/doctype/serial_no/serial_no.py +292,Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3},無法取消{0} {1},因為序列號{2}不屬於倉庫{3} -DocType: Company,Default Deferred Expense Account,默認遞延費用帳戶 +DocType: Company,Default Deferred Expense Account,默認遞延費用科目 apps/erpnext/erpnext/config/projects.py +29,Define Project type.,定義項目類型。 DocType: Supplier Scorecard,Weighting Function,加權函數 DocType: Healthcare Practitioner,OP Consulting Charge,OP諮詢費 @@ -767,7 +767,7 @@ apps/erpnext/erpnext/utilities/user_progress.py +28,Setup your ,設置你的 DocType: Student Report Generation Tool,Show Marks,顯示標記 DocType: Support Settings,Get Latest Query,獲取最新查詢 DocType: Quotation,Rate at which Price list currency is converted to company's base currency,價目表貨幣被換算成公司基礎貨幣的匯率 -apps/erpnext/erpnext/setup/doctype/company/company.py +74,Account {0} does not belong to company: {1},帳戶{0}不屬於公司:{1} +apps/erpnext/erpnext/setup/doctype/company/company.py +74,Account {0} does not belong to company: {1},科目{0}不屬於公司:{1} apps/erpnext/erpnext/setup/doctype/company/company.py +56,Abbreviation already used for another company,另一家公司已使用此縮寫 DocType: Selling Settings,Default Customer Group,預設客戶群組 DocType: Employee,IFSC Code,IFSC代碼 @@ -792,20 +792,20 @@ apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +76,Total C DocType: Installation Note Item,Installation Note Item,安裝注意項 DocType: Production Plan Item,Pending Qty,待定數量 apps/erpnext/erpnext/accounts/party.py +429,{0} {1} is not active,{0} {1}是不活動 -DocType: Woocommerce Settings,Freight and Forwarding Account,貨運和轉運帳戶 +DocType: Woocommerce Settings,Freight and Forwarding Account,貨運和轉運科目 apps/erpnext/erpnext/config/accounts.py +240,Setup cheque dimensions for printing,設置檢查尺寸打印 apps/erpnext/erpnext/hr/doctype/payroll_entry/payroll_entry.js +33,Create Salary Slips,創建工資單 DocType: Vital Signs,Bloated,脹 DocType: Salary Slip,Salary Slip Timesheet,工資單時間表 apps/erpnext/erpnext/controllers/buying_controller.py +200,Supplier Warehouse mandatory for sub-contracted Purchase Receipt,對於轉包的採購入庫單,供應商倉庫是強制性輸入的。 DocType: Sales Invoice,Total Commission,佣金總計 -DocType: Tax Withholding Account,Tax Withholding Account,扣繳稅款賬戶 +DocType: Tax Withholding Account,Tax Withholding Account,扣繳稅款科目 DocType: Pricing Rule,Sales Partner,銷售合作夥伴 apps/erpnext/erpnext/config/buying.py +150,All Supplier scorecards.,所有供應商記分卡。 DocType: Buying Settings,Purchase Receipt Required,需要採購入庫單 DocType: Delivery Note,Rail,軌 apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py +267,Target warehouse in row {0} must be same as Work Order,行{0}中的目標倉庫必須與工單相同 -apps/erpnext/erpnext/stock/doctype/item/item.py +183,Valuation Rate is mandatory if Opening Stock entered,估價費用是強制性的,如果打開股票進入 +apps/erpnext/erpnext/stock/doctype/item/item.py +183,Valuation Rate is mandatory if Opening Stock entered,估價費用是強制性的,如果打開庫存進入 apps/erpnext/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +143,No records found in the Invoice table,沒有在發票表中找到記錄 apps/erpnext/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +36,Please select Company and Party Type first,請選擇公司和黨的第一型 apps/erpnext/erpnext/accounts/doctype/pos_profile/pos_profile.py +31,"Already set default in pos profile {0} for user {1}, kindly disabled default",已經在用戶{1}的pos配置文件{0}中設置了默認值,請禁用默認值 @@ -828,7 +828,7 @@ apps/erpnext/erpnext/hr/doctype/attendance_request/attendance_request.py +18,Hal apps/erpnext/erpnext/public/js/pos/pos.html +4,Item Cart,項目車 apps/erpnext/erpnext/accounts/doctype/fiscal_year/fiscal_year.py +38,Fiscal Year Start Date should not be greater than Fiscal Year End Date,會計年度開始日期應不大於財政年度結束日期 DocType: Issue,Resolution,決議 -DocType: Employee,Personal Bio,個人生物 +DocType: Employee,Personal Bio,個人自傳 apps/erpnext/erpnext/non_profit/report/expiring_memberships/expiring_memberships.py +15,Membership ID,會員ID apps/erpnext/erpnext/templates/pages/order.html +77,Delivered: {0},交貨:{0} DocType: QuickBooks Migrator,Connected to QuickBooks,連接到QuickBooks @@ -868,7 +868,7 @@ DocType: Loan Application,Total Payable Interest,合計應付利息 apps/erpnext/erpnext/education/doctype/fee_schedule/fee_schedule.js +58,Total Outstanding: {0},總計:{0} DocType: Sales Invoice Timesheet,Sales Invoice Timesheet,銷售發票時間表 apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +150,Reference No & Reference Date is required for {0},參考號與參考日期須為{0} -DocType: Payroll Entry,Select Payment Account to make Bank Entry,選擇付款賬戶,使銀行進入 +DocType: Payroll Entry,Select Payment Account to make Bank Entry,選擇付款科目,使銀行進入 DocType: Hotel Settings,Default Invoice Naming Series,默認發票命名系列 apps/erpnext/erpnext/utilities/activation.py +136,"Create Employee records to manage leaves, expense claims and payroll",建立員工檔案管理葉,報銷和工資 apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +712,An error occurred during the update process,更新過程中發生錯誤 @@ -898,7 +898,7 @@ DocType: Timesheet,Billed,計費 DocType: Batch,Batch Description,批次說明 apps/erpnext/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.js +12,Creating student groups,創建學生組 apps/erpnext/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.js +12,Creating student groups,創建學生組 -apps/erpnext/erpnext/accounts/utils.py +763,"Payment Gateway Account not created, please create one manually.",支付網關帳戶沒有創建,請手動創建一個。 +apps/erpnext/erpnext/accounts/utils.py +763,"Payment Gateway Account not created, please create one manually.",支付閘道科目沒有創建,請手動創建一個。 apps/erpnext/erpnext/education/doctype/student_applicant/student_applicant.py +51,Not eligible for the admission in this program as per DOB,按照DOB的規定,沒有資格參加本計劃 DocType: Sales Invoice,Sales Taxes and Charges,銷售稅金及費用 DocType: Student,Sibling Details,兄弟姐妹詳情 @@ -922,7 +922,7 @@ apps/erpnext/erpnext/education/report/student_and_guardian_contact_details/stude apps/erpnext/erpnext/setup/setup_wizard/operations/install_fixtures.py +87,Manager,經理 apps/erpnext/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +8,From Fiscal Year,從財政年度開始 apps/erpnext/erpnext/selling/doctype/customer/customer.py +185,New credit limit is less than current outstanding amount for the customer. Credit limit has to be atleast {0},新的信用額度小於當前餘額為客戶著想。信用額度是ATLEAST {0} -apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +453,Please set account in Warehouse {0},請在倉庫{0}中設置帳戶 +apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +453,Please set account in Warehouse {0},請在倉庫{0}中設置會計科目 apps/erpnext/erpnext/controllers/trends.py +39,'Based On' and 'Group By' can not be same,“根據”和“分組依據”不能相同 DocType: Sales Person,Sales Person Targets,銷售人員目標 DocType: Work Order Operation,In minutes,在幾分鐘內 @@ -970,7 +970,7 @@ apps/erpnext/erpnext/config/accounts.py +293,To make recurring documents,複製 DocType: Loan,Total Interest Payable,合計應付利息 DocType: Landed Cost Taxes and Charges,Landed Cost Taxes and Charges,到岸成本稅費 DocType: Work Order Operation,Actual Start Time,實際開始時間 -DocType: Purchase Invoice Item,Deferred Expense Account,遞延費用帳戶 +DocType: Purchase Invoice Item,Deferred Expense Account,遞延費用科目 DocType: BOM Operation,Operation Time,操作時間 apps/erpnext/erpnext/manufacturing/doctype/work_order/work_order.js +466,Finish,完 apps/erpnext/erpnext/hr/doctype/salary_structure/salary_structure.js +441,Base,基礎 @@ -979,7 +979,7 @@ DocType: Travel Itinerary,Travel To,前往 apps/erpnext/erpnext/selling/page/point_of_sale/point_of_sale.js +1659,Write Off Amount,核銷金額 DocType: Leave Block List Allow,Allow User,允許用戶 DocType: Journal Entry,Bill No,帳單號碼 -DocType: Company,Gain/Loss Account on Asset Disposal,在資產處置收益/損失帳戶 +DocType: Company,Gain/Loss Account on Asset Disposal,在資產處置收益/損失科目 DocType: Vehicle Log,Service Details,服務細節 DocType: Vehicle Log,Service Details,服務細節 DocType: Lab Test Template,Grouped,分組 @@ -1013,7 +1013,7 @@ apps/erpnext/erpnext/hr/doctype/salary_structure/salary_structure.js +408,Previe apps/erpnext/erpnext/accounts/doctype/budget/budget.py +64,Account {0} has been entered multiple times,帳戶{0}已多次輸入 DocType: Account,Expenses Included In Valuation,支出計入估值 apps/erpnext/erpnext/non_profit/doctype/membership/membership.py +38,You can only renew if your membership expires within 30 days,如果您的會員資格在30天內到期,您只能續訂 -DocType: Shopping Cart Settings,Show Stock Availability,顯示股票可用性 +DocType: Shopping Cart Settings,Show Stock Availability,顯示庫存可用性 apps/erpnext/erpnext/assets/doctype/asset/asset.py +519,Set {0} in asset category {1} or company {2},在資產類別{1}或公司{2}中設置{0} DocType: Location,Longitude,經度 ,Absent Student Report,缺席學生報告 @@ -1038,7 +1038,7 @@ apps/erpnext/erpnext/education/doctype/student_group/student_group.py +24,Please DocType: Project,Estimated Cost,估計成本 DocType: Request for Quotation,Link to material requests,鏈接到材料請求 DocType: Journal Entry,Credit Card Entry,信用卡進入 -apps/erpnext/erpnext/config/accounts.py +35,Company and Accounts,公司與賬戶 +apps/erpnext/erpnext/config/accounts.py +35,Company and Accounts,公司與科目 apps/erpnext/erpnext/stock/report/stock_balance/stock_balance.py +85,In Value,在數值 DocType: Asset Settings,Depreciation Options,折舊選項 apps/erpnext/erpnext/assets/doctype/asset_movement/asset_movement.py +28,Either location or employee must be required,必須要求地點或員工 @@ -1055,7 +1055,7 @@ DocType: Purchase Order,Supply Raw Materials,供應原料 apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +10,Current Assets,流動資產 apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py +161,{0} is not a stock Item,{0}不是庫存項目 apps/erpnext/erpnext/hr/notification/training_feedback/training_feedback.html +6,Please share your feedback to the training by clicking on 'Training Feedback' and then 'New',請通過點擊“培訓反饋”,然後點擊“新建” -DocType: Mode of Payment Account,Default Account,預設帳戶 +DocType: Mode of Payment Account,Default Account,預設科目 apps/erpnext/erpnext/stock/doctype/item/item.py +302,Please select Sample Retention Warehouse in Stock Settings first,請先在庫存設置中選擇樣品保留倉庫 apps/erpnext/erpnext/accounts/doctype/loyalty_program/loyalty_program.js +62,Please select the Multiple Tier Program type for more than one collection rules.,請為多個收集規則選擇多層程序類型。 DocType: Payment Entry,Received Amount (Company Currency),收到的款項(公司幣種) @@ -1069,7 +1069,7 @@ DocType: Work Order Operation,Planned End Time,計劃結束時間 apps/erpnext/erpnext/accounts/doctype/account/account.py +100,Account with existing transaction cannot be converted to ledger,帳戶與現有的交易不能被轉換為總賬 apps/erpnext/erpnext/config/non_profit.py +33,Memebership Type Details,Memebership類型詳細信息 DocType: Delivery Note,Customer's Purchase Order No,客戶的採購訂單編號 -DocType: Clinical Procedure,Consume Stock,消費股票 +DocType: Clinical Procedure,Consume Stock,消費庫存 DocType: Budget,Budget Against,反對財政預算案 apps/erpnext/erpnext/stock/reorder_item.py +194,Auto Material Requests Generated,汽車材料的要求生成 apps/erpnext/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js +7,Lost,丟失 @@ -1084,7 +1084,7 @@ DocType: Special Test Items,Particulars,細節 apps/erpnext/erpnext/hr/doctype/leave_application/leave_application.py +23,{0}: From {0} of type {1},{0}:從{0}類型{1} apps/erpnext/erpnext/controllers/buying_controller.py +399,Row {0}: Conversion Factor is mandatory,列#{0}:轉換係數是強制性的 apps/erpnext/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +353,"Multiple Price Rules exists with same criteria, please resolve conflict by assigning priority. Price Rules: {0}",海報價格規則,同樣的標準存在,請通過分配優先解決衝突。價格規則:{0} -DocType: Exchange Rate Revaluation,Exchange Rate Revaluation Account,匯率重估賬戶 +DocType: Exchange Rate Revaluation,Exchange Rate Revaluation Account,匯率重估科目 apps/erpnext/erpnext/manufacturing/doctype/bom/bom.py +539,Cannot deactivate or cancel BOM as it is linked with other BOMs,無法關閉或取消BOM,因為它是與其他材料明細表鏈接 apps/erpnext/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js +106,Please select Company and Posting Date to getting entries,請選擇公司和發布日期以獲取條目 DocType: Asset,Maintenance,維護 @@ -1146,9 +1146,9 @@ apps/erpnext/erpnext/regional/report/eway_bill/eway_bill.py +163,Doc Name,文件 DocType: Expense Claim Detail,Expense Claim Type,費用報銷型 DocType: Shopping Cart Settings,Default settings for Shopping Cart,對購物車的預設設定 apps/erpnext/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.js +27,Add Timeslots,添加時代 -apps/erpnext/erpnext/stock/__init__.py +57,Please set Account in Warehouse {0} or Default Inventory Account in Company {1},請在倉庫{0}中設置帳戶或在公司{1}中設置默認庫存帳戶 +apps/erpnext/erpnext/stock/__init__.py +57,Please set Account in Warehouse {0} or Default Inventory Account in Company {1},請在倉庫{0}中設科目或在公司{1}中設置默認庫存科目 apps/erpnext/erpnext/assets/doctype/asset/depreciation.py +143,Asset scrapped via Journal Entry {0},通過資產日記帳分錄報廢{0} -DocType: Loan,Interest Income Account,利息收入賬戶 +DocType: Loan,Interest Income Account,利息收入科目 apps/erpnext/erpnext/hr/doctype/salary_structure/salary_structure.py +61,Max benefits should be greater than zero to dispense benefits,最大的好處應該大於零來分配好處 apps/erpnext/erpnext/non_profit/doctype/grant_application/grant_application.py +58,Review Invitation Sent,審核邀請已發送 DocType: Shift Assignment,Shift Assignment,班次分配 @@ -1167,7 +1167,7 @@ DocType: Account,Liability,責任 apps/erpnext/erpnext/hr/doctype/expense_claim/expense_claim.py +228,Sanctioned Amount cannot be greater than Claim Amount in Row {0}.,制裁金額不能大於索賠額行{0}。 apps/erpnext/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.html +14,Academic Term: ,學術期限: DocType: Salary Component,Do not include in total,不包括在內 -DocType: Company,Default Cost of Goods Sold Account,銷貨帳戶的預設成本 +DocType: Company,Default Cost of Goods Sold Account,銷貨成本科目 apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py +1287,Sample quantity {0} cannot be more than received quantity {1},採樣數量{0}不能超過接收數量{1} apps/erpnext/erpnext/stock/get_item_details.py +523,Price List not selected,未選擇價格列表 DocType: Request for Quotation Supplier,Send Email,發送電子郵件 @@ -1176,7 +1176,7 @@ DocType: Item,Max Sample Quantity,最大樣品量 apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +810,No Permission,無權限 DocType: Contract Fulfilment Checklist,Contract Fulfilment Checklist,合同履行清單 DocType: Vital Signs,Heart Rate / Pulse,心率/脈搏 -DocType: Company,Default Bank Account,預設銀行帳戶 +DocType: Company,Default Bank Account,預設銀行會計科目 apps/erpnext/erpnext/accounts/report/general_ledger/general_ledger.py +76,"To filter based on Party, select Party Type first",要根據黨的篩選,選擇黨第一類型 apps/erpnext/erpnext/controllers/sales_and_purchase_return.py +46,'Update Stock' can not be checked because items are not delivered via {0},不能勾選`更新庫存',因為項目未交付{0} DocType: Vehicle,Acquisition Date,採集日期 @@ -1202,7 +1202,7 @@ DocType: Item,Website Warehouse,網站倉庫 DocType: Payment Reconciliation,Minimum Invoice Amount,最小發票金額 apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +112,{0} {1}: Cost Center {2} does not belong to Company {3},{0} {1}:成本中心{2}不屬於公司{3} apps/erpnext/erpnext/utilities/user_progress.py +92,Upload your letter head (Keep it web friendly as 900px by 100px),上傳你的信頭(保持網頁友好,900px乘100px) -apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +89,{0} {1}: Account {2} cannot be a Group,{0} {1}帳戶{2}不能是一個組 +apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +89,{0} {1}: Account {2} cannot be a Group,{0} {1}科目{2}不能是一個群組科目 apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +367,Timesheet {0} is already completed or cancelled,時間表{0}已完成或取消 apps/erpnext/erpnext/templates/pages/projects.html +42,No tasks,沒有任務 apps/erpnext/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +81,Sales Invoice {0} created as paid,銷售發票{0}已創建為已付款 @@ -1282,7 +1282,7 @@ apps/erpnext/erpnext/projects/report/project_wise_stock_tracking/project_wise_st apps/erpnext/erpnext/config/selling.py +332,Point-of-Sale,銷售點 DocType: Fee Schedule,Fee Creation Status,費用創建狀態 DocType: Vehicle Log,Odometer Reading,里程表讀數 -apps/erpnext/erpnext/accounts/doctype/account/account.py +123,"Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'",帳戶餘額已歸為信用帳戶,不允許設為借方帳戶 +apps/erpnext/erpnext/accounts/doctype/account/account.py +123,"Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'",科目餘額已歸為貸方,不允許設為借方 DocType: Account,Balance must be,餘額必須 DocType: Notification Control,Expense Claim Rejected Message,報銷回絕訊息 ,Available Qty,可用數量 @@ -1349,9 +1349,9 @@ apps/erpnext/erpnext/stock/report/item_prices/item_prices.py +40,Sales Price Lis DocType: Healthcare Settings,"If checked, a customer will be created, mapped to Patient. Patient Invoices will be created against this Customer. You can also select existing Customer while creating Patient.",如果選中,將創建一個客戶,映射到患者。將針對該客戶創建病人發票。您也可以在創建患者時選擇現有客戶。 apps/erpnext/erpnext/accounts/doctype/loyalty_program/loyalty_program.py +63,Customer isn't enrolled in any Loyalty Program,客戶未加入任何忠誠度計劃 -DocType: Bank Reconciliation,Account Currency,賬戶幣種 +DocType: Bank Reconciliation,Account Currency,科目幣種 DocType: Lab Test,Sample ID,樣品編號 -apps/erpnext/erpnext/accounts/general_ledger.py +178,Please mention Round Off Account in Company,請註明舍入賬戶的公司 +apps/erpnext/erpnext/accounts/general_ledger.py +178,Please mention Round Off Account in Company,請註明舍入科目的公司 DocType: Purchase Receipt,Range,範圍 DocType: Supplier,Default Payable Accounts,預設應付帳款 apps/erpnext/erpnext/hr/doctype/attendance/attendance.py +52,Employee {0} is not active or does not exist,員工{0}不活躍或不存在 @@ -1471,7 +1471,7 @@ DocType: Repayment Schedule,Balance Loan Amount,平衡貸款額 apps/erpnext/erpnext/hr/doctype/employee_promotion/employee_promotion.js +132,Added to details,添加到細節 apps/erpnext/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js +14,Schedule Course,課程時間表 DocType: Budget,Applicable on Material Request,適用於材料請求 -apps/erpnext/erpnext/setup/setup_wizard/operations/install_fixtures.py +194,Stock Options,股票期權 +apps/erpnext/erpnext/setup/setup_wizard/operations/install_fixtures.py +194,Stock Options,庫存期權 apps/erpnext/erpnext/selling/page/point_of_sale/point_of_sale.js +628,No Items added to cart,沒有項目已添加到購物車 DocType: Journal Entry Account,Expense Claim,報銷 apps/erpnext/erpnext/assets/doctype/asset/asset.js +352,Do you really want to restore this scrapped asset?,難道你真的想恢復這個報廢的資產? @@ -1491,7 +1491,7 @@ DocType: Landed Cost Purchase Receipt,Landed Cost Purchase Receipt,到岸成本 DocType: Company,Default Terms,默認條款 DocType: Supplier Scorecard Period,Criteria,標準 DocType: Packing Slip Item,Packing Slip Item,包裝單項目 -DocType: Purchase Invoice,Cash/Bank Account,現金/銀行帳戶 +DocType: Purchase Invoice,Cash/Bank Account,現金/銀行會計科目 DocType: Travel Itinerary,Train,培養 apps/erpnext/erpnext/public/js/queries.js +96,Please specify a {0},請指定{0} apps/erpnext/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +74,Removed items with no change in quantity or value.,刪除的項目在數量或價值沒有變化。 @@ -1516,7 +1516,7 @@ DocType: Agriculture Task,Urgent,緊急 apps/erpnext/erpnext/accounts/doctype/payment_entry/payment_entry.js +187,Please specify a valid Row ID for row {0} in table {1},請指定行{0}在表中的有效行ID {1} apps/erpnext/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py +84,Unable to find variable: ,無法找到變量: apps/erpnext/erpnext/selling/page/point_of_sale/point_of_sale.js +893,Please select a field to edit from numpad,請選擇要從數字鍵盤編輯的字段 -apps/erpnext/erpnext/stock/doctype/item/item.py +293,Cannot be a fixed asset item as Stock Ledger is created.,不能成為股票分類賬創建的固定資產項目。 +apps/erpnext/erpnext/stock/doctype/item/item.py +293,Cannot be a fixed asset item as Stock Ledger is created.,不能成為庫存分類賬創建的固定資產項目。 apps/erpnext/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js +7,Admit,承認 apps/erpnext/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html +23,Go to the Desktop and start using ERPNext,轉到桌面和開始使用ERPNext apps/erpnext/erpnext/templates/pages/order.js +31,Pay Remaining,支付剩餘 @@ -1563,7 +1563,7 @@ DocType: Buying Settings,Material Transferred for Subcontract,轉包材料轉讓 DocType: Email Digest,Purchase Orders Items Overdue,採購訂單項目逾期 apps/erpnext/erpnext/accounts/page/pos/pos.js +1638,ZIP Code,郵政編碼 apps/erpnext/erpnext/controllers/selling_controller.py +265,Sales Order {0} is {1},銷售訂單{0} {1} -apps/erpnext/erpnext/hr/doctype/payroll_entry/payroll_entry.py +260,Select interest income account in loan {0},選擇貸款{0}中的利息收入帳戶 +apps/erpnext/erpnext/hr/doctype/payroll_entry/payroll_entry.py +260,Select interest income account in loan {0},選擇貸款{0}中的利息收入科目 DocType: Opportunity,Contact Info,聯絡方式 apps/erpnext/erpnext/config/stock.py +322,Making Stock Entries,製作Stock條目 apps/erpnext/erpnext/hr/doctype/employee_promotion/employee_promotion.py +15,Cannot promote Employee with status Left,無法提升狀態為Left的員工 @@ -1681,10 +1681,10 @@ apps/erpnext/erpnext/accounts/page/pos/pos.js +2530,"Payment Mode is not configu apps/erpnext/erpnext/buying/utils.py +74,Same item cannot be entered multiple times.,同一項目不能輸入多次。 apps/erpnext/erpnext/accounts/doctype/account/account_tree.js +30,"Further accounts can be made under Groups, but entries can be made against non-Groups",進一步帳戶可以根據組進行,但條目可針對非組進行 DocType: Lead,Lead,潛在客戶 -DocType: Email Digest,Payables,應付賬款 +DocType: Email Digest,Payables,應付帳款 DocType: Course,Course Intro,課程介紹 DocType: Amazon MWS Settings,MWS Auth Token,MWS Auth Token -apps/erpnext/erpnext/stock/doctype/batch/batch.js +105,Stock Entry {0} created,股票輸入{0}創建 +apps/erpnext/erpnext/stock/doctype/batch/batch.js +105,Stock Entry {0} created,庫存輸入{0}創建 apps/erpnext/erpnext/accounts/doctype/loyalty_program/loyalty_program.py +110,You don't have enought Loyalty Points to redeem,您沒有獲得忠誠度積分兌換 apps/erpnext/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +23,Please set associated account in Tax Withholding Category {0} against Company {1},請在針對公司{1}的預扣稅分類{0}中設置關聯帳戶 apps/erpnext/erpnext/controllers/buying_controller.py +405,Row #{0}: Rejected Qty can not be entered in Purchase Return,行#{0}:駁回採購退貨數量不能進入 @@ -1764,11 +1764,11 @@ DocType: Work Order,Qty To Manufacture,製造數量 DocType: Buying Settings,Maintain same rate throughout purchase cycle,在整個採購週期價格保持一致 DocType: Opportunity Item,Opportunity Item,項目的機會 ,Student and Guardian Contact Details,學生和監護人聯繫方式 -apps/erpnext/erpnext/accounts/doctype/account/account.js +51,Merge Account,合併帳戶 +apps/erpnext/erpnext/accounts/doctype/account/account.js +51,Merge Account,合併科目 apps/erpnext/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +53,Row {0}: For supplier {0} Email Address is required to send email,行{0}:對於供應商{0}的電郵地址發送電子郵件 apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +76,Temporary Opening,臨時開通 ,Employee Leave Balance,員工休假餘額 -apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +148,Balance for Account {0} must always be {1},帳戶{0}的餘額必須始終為{1} +apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +148,Balance for Account {0} must always be {1},科目{0}的餘額必須始終為{1} DocType: Patient Appointment,More Info,更多訊息 apps/erpnext/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +181,Valuation Rate required for Item in row {0},行對項目所需的估值速率{0} DocType: Supplier Scorecard,Scorecard Actions,記分卡操作 @@ -1841,7 +1841,7 @@ DocType: Purchase Invoice Item,Item Tax Rate,項目稅率 apps/erpnext/erpnext/regional/report/eway_bill/eway_bill.py +176,From Party Name,來自黨名 DocType: Student Group Student,Group Roll Number,組卷編號 DocType: Student Group Student,Group Roll Number,組卷編號 -apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +177,"For {0}, only credit accounts can be linked against another debit entry",{0},只有貸方帳戶可以連接另一個借方分錄 +apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +177,"For {0}, only credit accounts can be linked against another debit entry",{0},只有貸方科目可以連接另一個借方分錄 apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +660,Delivery Note {0} is not submitted,送貨單{0}未提交 apps/erpnext/erpnext/stock/get_item_details.py +167,Item {0} must be a Sub-contracted Item,項{0}必須是一個小項目簽約 apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +44,Capital Equipments,資本設備 @@ -1856,7 +1856,7 @@ DocType: Employee,Department and Grade,部門和年級 DocType: Sales Invoice Item,Edit Description,編輯說明 ,Team Updates,團隊更新 apps/erpnext/erpnext/accounts/doctype/payment_order/payment_order.js +39,For Supplier,對供應商 -DocType: Account,Setting Account Type helps in selecting this Account in transactions.,設置帳戶類型有助於在交易中選擇該帳戶。 +DocType: Account,Setting Account Type helps in selecting this Account in transactions.,設置會計科目類型有助於在交易中選擇該科目。 DocType: Purchase Invoice,Grand Total (Company Currency),總計(公司貨幣) apps/erpnext/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js +9,Create Print Format,創建打印格式 apps/erpnext/erpnext/education/doctype/fee_schedule/fee_schedule_list.js +5,Fee Created,創建費用 @@ -1914,7 +1914,7 @@ DocType: Stock Settings,Naming Series Prefix,命名系列前綴 DocType: Appraisal Template Goal,Appraisal Template Goal,考核目標模板 DocType: Salary Component,Earning,盈利 DocType: Supplier Scorecard,Scoring Criteria,評分標準 -DocType: Purchase Invoice,Party Account Currency,黨的賬戶幣種 +DocType: Purchase Invoice,Party Account Currency,黨的科目幣種 DocType: Delivery Trip,Total Estimated Distance,總估計距離 ,BOM Browser,BOM瀏覽器 apps/erpnext/erpnext/templates/emails/training_event.html +13,Please update your status for this training event,請更新此培訓活動的狀態 @@ -1929,7 +1929,7 @@ DocType: Inpatient Occupancy,Check In,報到 DocType: Maintenance Schedule Item,No of Visits,沒有訪問量的 apps/erpnext/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +165,Maintenance Schedule {0} exists against {1},針對{1}存在維護計劃{0} apps/erpnext/erpnext/education/doctype/student_applicant/student_applicant.js +36,Enrolling student,招生學生 -apps/erpnext/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +33,Currency of the Closing Account must be {0},在關閉帳戶的貨幣必須是{0} +apps/erpnext/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +33,Currency of the Closing Account must be {0},關閉科目的貨幣必須是{0} apps/erpnext/erpnext/hr/doctype/appraisal_template/appraisal_template.py +21,Sum of points for all goals should be 100. It is {0},對所有目標點的總和應該是100。{0} DocType: Project,Start and End Dates,開始和結束日期 DocType: Contract Template Fulfilment Terms,Contract Template Fulfilment Terms,合同模板履行條款 @@ -2027,7 +2027,7 @@ DocType: Job Opening,"Job profile, qualifications required etc.",所需的工作 DocType: Journal Entry Account,Account Balance,帳戶餘額 apps/erpnext/erpnext/config/accounts.py +179,Tax Rule for transactions.,稅收規則進行的交易。 DocType: Rename Tool,Type of document to rename.,的文件類型進行重命名。 -apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +53,{0} {1}: Customer is required against Receivable account {2},{0} {1}:需要客戶對應收賬款{2} +apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +53,{0} {1}: Customer is required against Receivable account {2},{0} {1}:需要客戶對應收帳款{2} DocType: Purchase Invoice,Total Taxes and Charges (Company Currency),總稅費和費用(公司貨幣) DocType: Weather,Weather Parameter,天氣參數 apps/erpnext/erpnext/accounts/report/trial_balance/trial_balance.js +76,Show unclosed fiscal year's P&L balances,顯示未關閉的會計年度的盈虧平衡 @@ -2036,8 +2036,8 @@ apps/erpnext/erpnext/regional/india/utils.py +179,House rented dates should be a DocType: Clinical Procedure Template,Collection Details,收集細節 DocType: POS Profile,Allow Print Before Pay,付款前允許打印 DocType: Linked Soil Texture,Linked Soil Texture,連接的土壤紋理 -DocType: Shipping Rule,Shipping Account,送貨帳戶 -apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +93,{0} {1}: Account {2} is inactive,{0} {1}帳戶{2}無效 +DocType: Shipping Rule,Shipping Account,送貨科目 +apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +93,{0} {1}: Account {2} is inactive,{0} {1}科目{2}無效 apps/erpnext/erpnext/utilities/activation.py +82,Make Sales Orders to help you plan your work and deliver on-time,製作銷售訂單,以幫助你計劃你的工作和按時交付 DocType: Bank Statement Transaction Entry,Bank Transaction Entries,銀行交易分錄 DocType: Quality Inspection,Readings,閱讀 @@ -2130,7 +2130,7 @@ DocType: Timesheet Detail,Expected Hrs,預計的小時數 apps/erpnext/erpnext/config/non_profit.py +28,Memebership Details,Memebership細節 DocType: Leave Block List,Block Holidays on important days.,重要的日子中封鎖假期。 apps/erpnext/erpnext/healthcare/doctype/lab_test/lab_test.js +194,Please input all required Result Value(s),請輸入所有必需的結果值(s) -apps/erpnext/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +142,Accounts Receivable Summary,應收賬款匯總 +apps/erpnext/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +142,Accounts Receivable Summary,應收帳款匯總 DocType: POS Closing Voucher,Linked Invoices,鏈接的發票 DocType: Loan,Monthly Repayment Amount,每月還款額 apps/erpnext/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool_dashboard.html +9,Opening Invoices,打開發票 @@ -2257,11 +2257,11 @@ DocType: Loan,Applicant Type,申請人類型 DocType: Purchase Invoice,03-Deficiency in services,03-服務不足 DocType: Healthcare Settings,Default Medical Code Standard,默認醫療代碼標準 apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +267,Purchase Receipt {0} is not submitted,採購入庫單{0}未提交 -DocType: Company,Default Payable Account,預設應付賬款 +DocType: Company,Default Payable Account,預設應付帳款科目 apps/erpnext/erpnext/config/website.py +17,"Settings for online shopping cart such as shipping rules, price list etc.",設定線上購物車,如航運規則,價格表等 apps/erpnext/erpnext/controllers/website_list_for_contact.py +113,{0}% Billed,{0}%已開立帳單 apps/erpnext/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py +73,Reserved Qty,保留數量 -DocType: Party Account,Party Account,黨的帳戶 +DocType: Party Account,Party Account,參與者科目 apps/erpnext/erpnext/hr/doctype/staffing_plan/staffing_plan.py +142,Please select Company and Designation,請選擇公司和指定 apps/erpnext/erpnext/config/setup.py +116,Human Resources,人力資源 apps/erpnext/erpnext/education/doctype/student_applicant/student_applicant.js +17,Reject,拒絕 @@ -2283,7 +2283,7 @@ DocType: Customer,Default Price List,預設價格表 apps/erpnext/erpnext/assets/doctype/asset/asset.py +492,Asset Movement record {0} created,資產運動記錄{0}創建 apps/erpnext/erpnext/utilities/page/leaderboard/leaderboard.js +175,No items found.,未找到任何項目。 apps/erpnext/erpnext/accounts/doctype/fiscal_year/fiscal_year.py +51,You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings,您不能刪除會計年度{0}。會計年度{0}設置為默認的全局設置 -DocType: Share Transfer,Equity/Liability Account,股票/負債賬戶 +DocType: Share Transfer,Equity/Liability Account,庫存/負債科目 apps/erpnext/erpnext/setup/doctype/customer_group/customer_group.py +20,A customer with the same name already exists,一個同名的客戶已經存在 DocType: Contract,Inactive,待用 apps/erpnext/erpnext/hr/doctype/payroll_entry/payroll_entry.js +224,This will submit Salary Slips and create accrual Journal Entry. Do you want to proceed?,這將提交工資單,並創建權責發生製日記賬分錄。你想繼續嗎? @@ -2292,7 +2292,7 @@ DocType: Purchase Order,Order Confirmation No,訂單確認號 DocType: Purchase Invoice,Eligibility For ITC,適用於ITC的資格 DocType: Journal Entry,Entry Type,條目類型 ,Customer Credit Balance,客戶信用平衡 -apps/erpnext/erpnext/accounts/report/cash_flow/cash_flow.py +82,Net Change in Accounts Payable,應付賬款淨額變化 +apps/erpnext/erpnext/accounts/report/cash_flow/cash_flow.py +82,Net Change in Accounts Payable,應付帳款淨額變化 apps/erpnext/erpnext/selling/doctype/customer/customer.py +257,Credit limit has been crossed for customer {0} ({1}/{2}),客戶{0}({1} / {2})的信用額度已超過 apps/erpnext/erpnext/setup/doctype/authorization_rule/authorization_rule.py +42,Customer required for 'Customerwise Discount',需要' Customerwise折扣“客戶 apps/erpnext/erpnext/config/accounts.py +136,Update bank payment dates with journals.,更新與日記帳之銀行付款日期。 @@ -2313,7 +2313,7 @@ DocType: Special Test Template,Result Component,結果組件 apps/erpnext/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js +46,Warranty Claim,保修索賠 ,Lead Details,潛在客戶詳情 DocType: Salary Slip,Loan repayment,償還借款 -DocType: Share Transfer,Asset Account,資產賬戶 +DocType: Share Transfer,Asset Account,資產科目 DocType: Purchase Invoice,End date of current invoice's period,當前發票的期限的最後一天 DocType: Pricing Rule,Applicable For,適用 DocType: Lab Test,Technician Name,技術員姓名 @@ -2347,7 +2347,7 @@ DocType: Leave Type,Earned Leave,獲得休假 DocType: Employee,Salary Details,薪資明細 DocType: Territory,Territory Manager,區域經理 DocType: Packed Item,To Warehouse (Optional),倉庫(可選) -DocType: GST Settings,GST Accounts,GST賬戶 +DocType: GST Settings,GST Accounts,GST科目 DocType: Payment Entry,Paid Amount (Company Currency),支付的金額(公司貨幣) DocType: Purchase Invoice,Additional Discount,更多優惠 DocType: Selling Settings,Selling Settings,銷售設置 @@ -2382,12 +2382,12 @@ DocType: Material Request,Transferred,轉入 DocType: Vehicle,Doors,門 apps/erpnext/erpnext/setup/setup_wizard/operations/defaults_setup.py +118,ERPNext Setup Complete!,ERPNext設定完成! DocType: Healthcare Settings,Collect Fee for Patient Registration,收取病人登記費 -apps/erpnext/erpnext/stock/doctype/item/item.py +737,Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item,股票交易後不能更改屬性。創建一個新項目並將庫存轉移到新項目 +apps/erpnext/erpnext/stock/doctype/item/item.py +737,Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item,庫存交易後不能更改屬性。創建一個新項目並將庫存轉移到新項目 DocType: Course Assessment Criteria,Weightage,權重 DocType: Purchase Invoice,Tax Breakup,稅收分解 DocType: Employee,Joining Details,加入詳情 DocType: Member,Non Profit Member,非盈利會員 -apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +67,{0} {1}: Cost Center is required for 'Profit and Loss' account {2}. Please set up a default Cost Center for the Company.,{0} {1}:需要的損益“賬戶成本中心{2}。請設置為公司默認的成本中心。 +apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +67,{0} {1}: Cost Center is required for 'Profit and Loss' account {2}. Please set up a default Cost Center for the Company.,{0} {1}:需要的損益“科目成本中心{2}。請設置為公司默認的成本中心。 apps/erpnext/erpnext/selling/doctype/customer/customer.py +175,A Customer Group exists with same name please change the Customer name or rename the Customer Group,客戶群組存在相同名稱,請更改客戶名稱或重新命名客戶群組 DocType: Location,Area,區 apps/erpnext/erpnext/public/js/templates/contact_list.html +37,New Contact,新建聯絡人 @@ -2474,11 +2474,11 @@ apps/erpnext/erpnext/selling/page/point_of_sale/point_of_sale.js +901,Discount a apps/erpnext/erpnext/accounts/doctype/cost_center/cost_center_tree.js +26,"Number of new Cost Center, it will be included in the cost center name as a prefix",新成本中心的數量,它將作為前綴包含在成本中心名稱中 DocType: Sales Order,To Deliver and Bill,準備交貨及開立發票 DocType: Student Group,Instructors,教師 -DocType: GL Entry,Credit Amount in Account Currency,在賬戶幣金額 +DocType: GL Entry,Credit Amount in Account Currency,在科目幣金額 apps/erpnext/erpnext/manufacturing/doctype/bom/bom.py +631,BOM {0} must be submitted,BOM {0}必須提交 DocType: Authorization Control,Authorization Control,授權控制 apps/erpnext/erpnext/controllers/buying_controller.py +416,Row #{0}: Rejected Warehouse is mandatory against rejected Item {1},行#{0}:拒絕倉庫是強制性的反對否決項{1} -apps/erpnext/erpnext/controllers/stock_controller.py +96,"Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.",倉庫{0}未與任何帳戶關聯,請在倉庫記錄中提及該帳戶,或在公司{1}中設置默認庫存帳戶。 +apps/erpnext/erpnext/controllers/stock_controller.py +96,"Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.",倉庫{0}未與任何科目關聯,請在倉庫記錄中設定科目,或在公司{1}中設置默認庫存科目。 apps/erpnext/erpnext/utilities/activation.py +81,Manage your orders,管理您的訂單 DocType: Work Order Operation,Actual Time and Cost,實際時間和成本 apps/erpnext/erpnext/stock/doctype/material_request/material_request.py +57,Material Request of maximum {0} can be made for Item {1} against Sales Order {2},針對銷售訂單{2}的項目{1},最多可以有 {0} 被完成。 @@ -2514,14 +2514,14 @@ DocType: SMS Center,Create Receiver List,創建接收器列表 apps/erpnext/erpnext/assets/doctype/asset/asset.py +98,Available-for-use Date should be after purchase date,可供使用的日期應在購買日期之後 DocType: Vehicle,Wheels,車輪 DocType: Packing Slip,To Package No.,以包號 -DocType: Sales Invoice Item,Deferred Revenue Account,遞延收入帳戶 +DocType: Sales Invoice Item,Deferred Revenue Account,遞延收入科目 DocType: Production Plan,Material Requests,材料要求 DocType: Warranty Claim,Issue Date,發行日期 DocType: Activity Cost,Activity Cost,項目成本 DocType: Sales Invoice Timesheet,Timesheet Detail,詳細時間表 DocType: Purchase Receipt Item Supplied,Consumed Qty,消耗的數量 apps/erpnext/erpnext/setup/setup_wizard/data/industry_type.py +52,Telecommunications,電信 -apps/erpnext/erpnext/accounts/party.py +292,Billing currency must be equal to either default company's currency or party account currency,帳單貨幣必須等於默認公司的貨幣或帳戶幣種 +apps/erpnext/erpnext/accounts/party.py +292,Billing currency must be equal to either default company's currency or party account currency,帳單貨幣必須等於默認公司的貨幣或科目幣種 DocType: Packing Slip,Indicates that the package is a part of this delivery (Only Draft),表示該包是這個交付的一部分(僅草案) apps/erpnext/erpnext/controllers/accounts_controller.py +786,Row {0}: Due Date cannot be before posting date,行{0}:到期日期不能在發布日期之前 apps/erpnext/erpnext/accounts/doctype/payment_request/payment_request.js +37,Make Payment Entry,製作付款分錄 @@ -2535,7 +2535,7 @@ apps/erpnext/erpnext/config/accounts.py +209,Tree of financial Cost Centers.,財 apps/erpnext/erpnext/regional/report/eway_bill/eway_bill.py +151,Sub Type,子類型 DocType: Serial No,Delivery Document No,交貨證明文件號碼 DocType: Sales Order Item,Ensure Delivery Based on Produced Serial No,確保基於生產的序列號的交貨 -apps/erpnext/erpnext/assets/doctype/asset/depreciation.py +197,Please set 'Gain/Loss Account on Asset Disposal' in Company {0},請公司制定“關於資產處置收益/損失帳戶”{0} +apps/erpnext/erpnext/assets/doctype/asset/depreciation.py +197,Please set 'Gain/Loss Account on Asset Disposal' in Company {0},請公司制定“關於資產處置收益/損失科目”{0} DocType: Landed Cost Voucher,Get Items From Purchase Receipts,從採購入庫單取得項目 DocType: Serial No,Creation Date,創建日期 apps/erpnext/erpnext/assets/doctype/asset_movement/asset_movement.py +55,Target Location is required for the asset {0},目標位置是資產{0}所必需的 @@ -2569,7 +2569,7 @@ DocType: Bank Guarantee,Margin Money,保證金 DocType: Budget,Budget,預算 apps/erpnext/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +83,Set Open,設置打開 apps/erpnext/erpnext/stock/doctype/item/item.py +287,Fixed Asset Item must be a non-stock item.,固定資產項目必須是一個非庫存項目。 -apps/erpnext/erpnext/accounts/doctype/budget/budget.py +60,"Budget cannot be assigned against {0}, as it's not an Income or Expense account",財政預算案不能對{0}指定的,因為它不是一個收入或支出帳戶 +apps/erpnext/erpnext/accounts/doctype/budget/budget.py +60,"Budget cannot be assigned against {0}, as it's not an Income or Expense account",財政預算案不能對{0}指定的,因為它不是一個收入或支出科目 apps/erpnext/erpnext/hr/utils.py +228,Max exemption amount for {0} is {1},{0}的最大免除金額為{1} apps/erpnext/erpnext/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py +51,Achieved,已實現 DocType: Student Admission,Application Form Route,申請表路線 @@ -2644,15 +2644,15 @@ DocType: Loan Application,Total Payable Amount,合計應付額 DocType: Task,Expected Time (in hours),預期時間(以小時計) DocType: Item Reorder,Check in (group),檢查(組) ,Qty to Order,訂購數量 -DocType: Period Closing Voucher,"The account head under Liability or Equity, in which Profit/Loss will be booked",負債或權益下的帳戶頭,其中利潤/虧損將被黃牌警告 -apps/erpnext/erpnext/accounts/doctype/budget/budget.py +44,Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4},對於財務年度{4},{1}'{2}'和帳戶“{3}”已存在另一個預算記錄“{0}” +DocType: Period Closing Voucher,"The account head under Liability or Equity, in which Profit/Loss will be booked",負債或權益下的科目頭,其中利潤/虧損將被黃牌警告 +apps/erpnext/erpnext/accounts/doctype/budget/budget.py +44,Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4},對於財務年度{4},{1}'{2}'和科目“{3}”已存在另一個預算記錄“{0}” apps/erpnext/erpnext/config/projects.py +36,Gantt chart of all tasks.,所有任務的甘特圖。 DocType: Opportunity,Mins to First Response,分鐘為第一個反應 DocType: Pricing Rule,Margin Type,保證金類型 apps/erpnext/erpnext/projects/doctype/project/project_dashboard.html +15,{0} hours,{0}小時 DocType: Course,Default Grading Scale,默認等級規模 DocType: Appraisal,For Employee Name,對於員工姓名 -DocType: Woocommerce Settings,Tax Account,稅收帳戶 +DocType: Woocommerce Settings,Tax Account,稅收科目 DocType: C-Form Invoice Detail,Invoice No,發票號碼 DocType: Room,Room Name,房間名稱 DocType: Prescription Duration,Prescription Duration,處方時間 @@ -2704,7 +2704,7 @@ apps/erpnext/erpnext/accounts/doctype/payment_entry/payment_entry.py +389,Amount ,Quotation Trends,報價趨勢 apps/erpnext/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +166,Item Group not mentioned in item master for item {0},項目{0}之項目主檔未提及之項目群組 DocType: GoCardless Mandate,GoCardless Mandate,GoCardless任務 -apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +440,Debit To account must be a Receivable account,借方帳戶必須是應收帳款帳戶 +apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +440,Debit To account must be a Receivable account,借方科目必須是應收帳款科目 DocType: Shipping Rule,Shipping Amount,航運量 DocType: Supplier Scorecard Period,Period Score,期間得分 apps/erpnext/erpnext/public/js/event.js +19,Add Customers,添加客戶 @@ -2777,7 +2777,7 @@ apps/erpnext/erpnext/accounts/doctype/fiscal_year/fiscal_year.py +22,{0} is now apps/erpnext/erpnext/projects/doctype/task/task.js +45,Expense Claims,報銷 DocType: Employee Tax Exemption Declaration,Total Exemption Amount,免稅總額 ,BOM Search,BOM搜索 -DocType: Project,Total Consumed Material Cost (via Stock Entry),總消耗材料成本(通過股票輸入) +DocType: Project,Total Consumed Material Cost (via Stock Entry),總消耗材料成本(通過庫存輸入) DocType: Subscription,Subscription Period,訂閱期 apps/erpnext/erpnext/hr/doctype/leave_application/leave_application.js +169,To Date cannot be less than From Date,迄今不能少於起始日期 DocType: Item,"Publish ""In Stock"" or ""Not in Stock"" on Hub based on stock available in this warehouse.",基於倉庫中的庫存,在Hub上發布“庫存”或“庫存”。 @@ -2786,7 +2786,7 @@ apps/erpnext/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_ DocType: Workstation,Wages per hour,時薪 apps/erpnext/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +47,Stock balance in Batch {0} will become negative {1} for Item {2} at Warehouse {3},在批量庫存餘額{0}將成為負{1}的在倉庫項目{2} {3} apps/erpnext/erpnext/templates/emails/reorder_item.html +1,Following Material Requests have been raised automatically based on Item's re-order level,下列資料的要求已自動根據項目的重新排序水平的提高 -apps/erpnext/erpnext/controllers/accounts_controller.py +376,Account {0} is invalid. Account Currency must be {1},帳戶{0}是無效的。帳戶貨幣必須是{1} +apps/erpnext/erpnext/controllers/accounts_controller.py +376,Account {0} is invalid. Account Currency must be {1},科目{0}是無效的。科目貨幣必須是{1} apps/erpnext/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.py +31,From Date {0} cannot be after employee's relieving Date {1},起始日期{0}不能在員工解除日期之後{1} DocType: Supplier,Is Internal Supplier,是內部供應商 DocType: Employee,Create User Permission,創建用戶權限 @@ -2813,7 +2813,7 @@ apps/erpnext/erpnext/setup/doctype/email_digest/email_digest.js +64,disabled use apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +970,Quotation,報價 apps/erpnext/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +1041,Cannot set a received RFQ to No Quote,無法將收到的詢價單設置為無報價 DocType: Salary Slip,Total Deduction,扣除總額 -apps/erpnext/erpnext/accounts/report/general_ledger/general_ledger.py +22,Select an account to print in account currency,選擇一個賬戶以賬戶貨幣進行打印 +apps/erpnext/erpnext/accounts/report/general_ledger/general_ledger.py +22,Select an account to print in account currency,選擇一個科目以科目貨幣進行打印 ,Production Analytics,生產Analytics(分析) apps/erpnext/erpnext/healthcare/doctype/patient/patient_dashboard.py +6,This is based on transactions against this Patient. See timeline below for details,這是基於對這個病人的交易。有關詳情,請參閱下面的時間表 apps/erpnext/erpnext/controllers/sales_and_purchase_return.py +136,Item {0} has already been returned,項{0}已被退回 @@ -2852,7 +2852,7 @@ DocType: BOM,Scrap Material Cost,廢料成本 apps/erpnext/erpnext/stock/doctype/serial_no/serial_no.py +243,Serial No {0} does not belong to any Warehouse,序列號{0}不屬於任何倉庫 DocType: Grant Application,Email Notification Sent,電子郵件通知已發送 DocType: Purchase Invoice,In Words (Company Currency),大寫(Company Currency) -apps/erpnext/erpnext/accounts/doctype/bank_account/bank_account.py +24,Company is manadatory for company account,公司是公司賬戶的管理者 +apps/erpnext/erpnext/accounts/doctype/bank_account/bank_account.py +24,Company is manadatory for company account,公司是公司科目的管理者 apps/erpnext/erpnext/buying/doctype/purchase_order/purchase_order.js +1092,"Item Code, warehouse, quantity are required on row",在行上需要項目代碼,倉庫,數量 DocType: Bank Guarantee,Supplier,供應商 apps/erpnext/erpnext/hr/doctype/department/department.js +9,This is a root department and cannot be edited.,這是根部門,無法編輯。 @@ -2861,7 +2861,7 @@ apps/erpnext/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py +53 apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +113,Miscellaneous Expenses,雜項開支 DocType: Global Defaults,Default Company,預設公司 DocType: Company,Transactions Annual History,交易年曆 -apps/erpnext/erpnext/controllers/stock_controller.py +231,Expense or Difference account is mandatory for Item {0} as it impacts overall stock value,對項目{0}而言, 費用或差異帳戶是強制必填的,因為它影響整個庫存總值。 +apps/erpnext/erpnext/controllers/stock_controller.py +231,Expense or Difference account is mandatory for Item {0} as it impacts overall stock value,對項目{0}而言, 費用或差異科目是強制必填的,因為它影響整個庫存總值。 DocType: Bank,Bank Name,銀行名稱 apps/erpnext/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +78,-Above,-以上 apps/erpnext/erpnext/selling/doctype/sales_order/sales_order.js +1301,Leave the field empty to make purchase orders for all suppliers,將該字段留空以為所有供應商下達採購訂單 @@ -2891,7 +2891,7 @@ DocType: Payment Entry,Unallocated Amount,未分配金額 apps/erpnext/erpnext/accounts/doctype/budget/budget.py +77,Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses,請啟用適用於採購訂單並適用於預訂實際費用 apps/erpnext/erpnext/templates/includes/product_page.js +101,Cannot find a matching Item. Please select some other value for {0}.,無法找到匹配的項目。請選擇其他值{0}。 DocType: POS Profile,Taxes and Charges,稅收和收費 -DocType: Item,"A Product or a Service that is bought, sold or kept in stock.",產品或服務已購買,出售或持有的股票。 +DocType: Item,"A Product or a Service that is bought, sold or kept in stock.",產品或服務已購買,出售或持有的庫存。 apps/erpnext/erpnext/hr/page/team_updates/team_updates.js +44,No more updates,沒有更多的更新 apps/erpnext/erpnext/accounts/doctype/payment_entry/payment_entry.js +184,Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row,不能選擇充電式為'在上一行量'或'在上一行總'的第一行 apps/erpnext/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py +6,This covers all scorecards tied to this Setup,這涵蓋了與此安裝程序相關的所有記分卡 @@ -2929,7 +2929,7 @@ apps/erpnext/erpnext/education/doctype/student_group_creation_tool/student_group apps/erpnext/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py +77,{0} Student Groups created.,{0}創建學生組。 DocType: Sales Invoice,Total Billing Amount,總結算金額 apps/erpnext/erpnext/education/doctype/fee_schedule/fee_schedule.py +50,Program in the Fee Structure and Student Group {0} are different.,費用結構和學生組{0}中的課程是不同的。 -DocType: Bank Statement Transaction Entry,Receivable Account,應收賬款 +DocType: Bank Statement Transaction Entry,Receivable Account,應收帳款 apps/erpnext/erpnext/stock/doctype/item_price/item_price.py +31,Valid From Date must be lesser than Valid Upto Date.,有效起始日期必須小於有效起始日期。 apps/erpnext/erpnext/controllers/accounts_controller.py +689,Row #{0}: Asset {1} is already {2},行#{0}:資產{1}已經是{2} DocType: Quotation Item,Stock Balance,庫存餘額 @@ -2939,13 +2939,13 @@ DocType: Expense Claim Detail,Expense Claim Detail,報銷詳情 DocType: Purchase Invoice,TRIPLICATE FOR SUPPLIER,供應商提供服務 DocType: Exchange Rate Revaluation Account,New Balance In Base Currency,基礎貨幣的新平衡 DocType: Crop Cycle,This will be day 1 of the crop cycle,這將是作物週期的第一天 -apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +915,Please select correct account,請選擇正確的帳戶 +apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +915,Please select correct account,請選擇正確的科目 DocType: Salary Structure Assignment,Salary Structure Assignment,薪酬結構分配 DocType: Purchase Invoice Item,Weight UOM,重量計量單位 apps/erpnext/erpnext/config/accounts.py +435,List of available Shareholders with folio numbers,包含folio號碼的可用股東名單 DocType: Salary Structure Employee,Salary Structure Employee,薪資結構員工 apps/erpnext/erpnext/stock/report/stock_balance/stock_balance.js +62,Show Variant Attributes,顯示變體屬性 -apps/erpnext/erpnext/accounts/doctype/payment_request/payment_request.py +48,The payment gateway account in plan {0} is different from the payment gateway account in this payment request,計劃{0}中的支付網關帳戶與此付款請求中的支付網關帳戶不同 +apps/erpnext/erpnext/accounts/doctype/payment_request/payment_request.py +48,The payment gateway account in plan {0} is different from the payment gateway account in this payment request,計劃{0}中的支付閘道科目與此付款請求中的支付閘道科目不同 DocType: Course,Course Name,課程名 apps/erpnext/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +50,No Tax Withholding data found for the current Fiscal Year.,未找到當前財年的預扣稅數據。 DocType: Employee Leave Approver,Users who can approve a specific employee's leave applications,用戶可以批准特定員工的休假申請 @@ -2989,14 +2989,14 @@ apps/erpnext/erpnext/templates/pages/product_search.html +3,Product Search,產 DocType: Cashier Closing,To Time,要時間 apps/erpnext/erpnext/hr/utils.py +202,) for {0},)為{0} DocType: Authorization Rule,Approving Role (above authorized value),批准角色(上述授權值) -apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +147,Credit To account must be a Payable account,信用帳戶必須是應付賬款 +apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +147,Credit To account must be a Payable account,信用科目必須是應付帳款 DocType: Loan,Total Amount Paid,總金額支付 DocType: Asset,Insurance End Date,保險終止日期 apps/erpnext/erpnext/education/doctype/student_applicant/student_applicant.py +43,Please select Student Admission which is mandatory for the paid student applicant,請選擇付費學生申請者必須入學的學生 apps/erpnext/erpnext/manufacturing/doctype/bom/bom.py +370,BOM recursion: {0} cannot be parent or child of {2},BOM遞歸: {0}不能父母或兒童{2} apps/erpnext/erpnext/accounts/doctype/cost_center/cost_center_tree.js +40,Budget List,預算清單 DocType: Work Order Operation,Completed Qty,完成數量 -apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +180,"For {0}, only debit accounts can be linked against another credit entry",{0},只有借方帳戶可以連接另一個貸方分錄 +apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +180,"For {0}, only debit accounts can be linked against another credit entry",{0},只有借方科目可以連接另一個貸方分錄 DocType: Manufacturing Settings,Allow Overtime,允許加班 apps/erpnext/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +149,"Serialized Item {0} cannot be updated using Stock Reconciliation, please use Stock Entry",序列化項目{0}無法使用庫存調節更新,請使用庫存條目 apps/erpnext/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +149,"Serialized Item {0} cannot be updated using Stock Reconciliation, please use Stock Entry",序列化項目{0}無法使用庫存調節更新,請使用庫存條目 @@ -3010,7 +3010,7 @@ apps/erpnext/erpnext/config/integrations.py +13,GoCardless payment gateway setti apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +129,Exchange Gain/Loss,兌換收益/損失 DocType: Opportunity,Lost Reason,失落的原因 DocType: Amazon MWS Settings,Enable Amazon,啟用亞馬遜 -apps/erpnext/erpnext/controllers/accounts_controller.py +322,Row #{0}: Account {1} does not belong to company {2},行#{0}:帳戶{1}不屬於公司{2} +apps/erpnext/erpnext/controllers/accounts_controller.py +322,Row #{0}: Account {1} does not belong to company {2},行#{0}:科目{1}不屬於公司{2} apps/erpnext/erpnext/setup/doctype/naming_series/naming_series.py +30,Unable to find DocType {0},無法找到DocType {0} DocType: Quality Inspection,Sample Size,樣本大小 apps/erpnext/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +47,Please enter Receipt Document,請輸入收據憑證 @@ -3056,7 +3056,7 @@ DocType: Timesheet Detail,Costing Amount,成本核算金額 DocType: Student Admission Program,Application Fee,報名費 apps/erpnext/erpnext/hr/doctype/payroll_entry/payroll_entry.js +75,Submit Salary Slip,提交工資單 apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js +13,On Hold,等候接聽 -DocType: Account,Inter Company Account,Inter公司帳戶 +DocType: Account,Inter Company Account,公司內帳戶 apps/erpnext/erpnext/stock/doctype/item_price/item_price.js +17,Import in Bulk,進口散裝 DocType: Sales Partner,Address & Contacts,地址及聯絡方式 DocType: SMS Log,Sender Name,發件人名稱 @@ -3108,7 +3108,7 @@ DocType: BOM,"Specify the operations, operating cost and give a unique Operation DocType: Travel Request,Any other details,任何其他細節 apps/erpnext/erpnext/controllers/status_updater.py +207,This document is over limit by {0} {1} for item {4}. Are you making another {3} against the same {2}?,這份文件是超過限制,通過{0} {1}項{4}。你在做另一個{3}對同一{2}? apps/erpnext/erpnext/public/js/controllers/transaction.js +1288,Please set recurring after saving,請設置保存後復發 -apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +874,Select change amount account,選擇變化量賬戶 +apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +874,Select change amount account,選擇變化量科目 DocType: Purchase Invoice,Price List Currency,價格表之貨幣 DocType: Naming Series,User must always select,用戶必須始終選擇 DocType: Stock Settings,Allow Negative Stock,允許負庫存 @@ -3148,7 +3148,7 @@ apps/erpnext/erpnext/accounts/report/general_ledger/general_ledger.py +56,Group apps/erpnext/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +391,Are you sure you want to cancel this appointment?,你確定要取消這個預約嗎? DocType: Hotel Room Pricing Package,Hotel Room Pricing Package,酒店房間價格套餐 apps/erpnext/erpnext/selling/page/sales_funnel/sales_funnel.js +42,Sales Pipeline,銷售渠道 -apps/erpnext/erpnext/hr/doctype/payroll_entry/payroll_entry.py +167,Please set default account in Salary Component {0},請薪酬部分設置默認帳戶{0} +apps/erpnext/erpnext/hr/doctype/payroll_entry/payroll_entry.py +167,Please set default account in Salary Component {0},請薪酬部分設置默認科目{0} apps/erpnext/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py +200,Please select BOM for Item in Row {0},請行選擇BOM為項目{0} apps/erpnext/erpnext/accounts/doctype/subscription/subscription.js +13,Fetch Subscription Updates,獲取訂閱更新 apps/erpnext/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py +28,Account {0} does not match with Company {1} in Mode of Account: {2},帳戶{0}與帳戶模式{2}中的公司{1}不符 @@ -3183,7 +3183,7 @@ apps/erpnext/erpnext/manufacturing/doctype/work_order/work_order.js +232,For Job apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +1556,Prescriptions,處方 DocType: Payment Gateway Account,Payment Account,付款帳號 apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +1112,Please specify Company to proceed,請註明公司以處理 -apps/erpnext/erpnext/accounts/report/cash_flow/cash_flow.py +81,Net Change in Accounts Receivable,應收賬款淨額變化 +apps/erpnext/erpnext/accounts/report/cash_flow/cash_flow.py +81,Net Change in Accounts Receivable,應收帳款淨額變化 apps/erpnext/erpnext/setup/setup_wizard/operations/install_fixtures.py +66,Compensatory Off,補假 DocType: Job Offer,Accepted,接受的 DocType: POS Closing Voucher,Sales Invoices Summary,銷售發票摘要 @@ -3225,7 +3225,7 @@ DocType: Support Search Source,Result Preview Field,結果預覽字段 DocType: Item Price,Packing Unit,包裝單位 DocType: Subscription,Trialling,試用 DocType: Sales Invoice Item,Deferred Revenue,遞延收入 -DocType: Shopify Settings,Cash Account will used for Sales Invoice creation,現金帳戶將用於創建銷售發票 +DocType: Shopify Settings,Cash Account will used for Sales Invoice creation,現金科目將用於創建銷售發票 DocType: Employee Tax Exemption Declaration Category,Exemption Sub Category,豁免子類別 DocType: Member,Membership Expiry Date,會員到期日 apps/erpnext/erpnext/controllers/sales_and_purchase_return.py +134,{0} must be negative in return document,{0}必須返回文檔中負 @@ -3355,17 +3355,17 @@ DocType: Employee Separation,Employee Separation,員工分離 DocType: BOM Item,Original Item,原始項目 DocType: Purchase Receipt Item,Recd Quantity,到貨數量 apps/erpnext/erpnext/education/doctype/program_enrollment/program_enrollment.py +64,Fee Records Created - {0},費紀錄創造 - {0} -DocType: Asset Category Account,Asset Category Account,資產類別的帳戶 +DocType: Asset Category Account,Asset Category Account,資產類別的科目 apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +1024,Row #{0} (Payment Table): Amount must be positive,行#{0}(付款表):金額必須為正值 apps/erpnext/erpnext/manufacturing/doctype/production_order/production_order.py +137,Cannot produce more Item {0} than Sales Order quantity {1},無法產生更多的項目{0}不是銷售訂單數量{1} apps/erpnext/erpnext/stock/doctype/item/item.js +446,Select Attribute Values,選擇屬性值 DocType: Purchase Invoice,Reason For Issuing document,簽發文件的原因 -apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +583,Stock Entry {0} is not submitted,股票輸入{0}不提交 -DocType: Payment Reconciliation,Bank / Cash Account,銀行/現金帳戶 +apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +583,Stock Entry {0} is not submitted,庫存輸入{0}不提交 +DocType: Payment Reconciliation,Bank / Cash Account,銀行/現金科目 apps/erpnext/erpnext/crm/doctype/lead/lead.py +47,Next Contact By cannot be same as the Lead Email Address,接著聯繫到不能相同鉛郵箱地址 DocType: Tax Rule,Billing City,結算城市 DocType: Asset,Manual,手冊 -DocType: Salary Component Account,Salary Component Account,薪金部分賬戶 +DocType: Salary Component Account,Salary Component Account,薪金部分科目 DocType: Global Defaults,Hide Currency Symbol,隱藏貨幣符號 apps/erpnext/erpnext/selling/page/sales_funnel/sales_funnel.js +281,Sales Opportunities by Source,來源的銷售機會 apps/erpnext/erpnext/config/accounts.py +287,"e.g. Bank, Cash, Credit Card",例如:銀行,現金,信用卡 @@ -3449,7 +3449,7 @@ DocType: Purchase Invoice Item,Received Qty,到貨數量 DocType: Stock Entry Detail,Serial No / Batch,序列號/批次 apps/erpnext/erpnext/selling/doctype/sales_order/sales_order.py +359,Not Paid and Not Delivered,沒有支付,未送達 DocType: Product Bundle,Parent Item,父項目 -DocType: Account,Account Type,帳戶類型 +DocType: Account,Account Type,科目類型 DocType: Shopify Settings,Webhooks Details,Webhooks詳細信息 apps/erpnext/erpnext/templates/pages/projects.html +58,No time sheets,沒有考勤表 DocType: GoCardless Mandate,GoCardless Customer,GoCardless客戶 @@ -3476,7 +3476,7 @@ apps/erpnext/erpnext/manufacturing/doctype/job_card/job_card.js +26,Start Job, apps/erpnext/erpnext/assets/doctype/asset_movement/asset_movement.py +32,Serial no is required for the asset {0},資產{0}需要序列號 apps/erpnext/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py +43,Disabled template must not be default template,殘疾人模板必須不能默認模板 apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.py +542,For row {0}: Enter planned qty,對於行{0}:輸入計劃的數量 -DocType: Account,Income Account,收入帳戶 +DocType: Account,Income Account,收入科目 DocType: Payment Request,Amount in customer's currency,量客戶的貨幣 apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +880,Delivery,交貨 DocType: Stock Reconciliation Item,Current Qty,目前數量 @@ -3488,7 +3488,7 @@ DocType: Appraisal Goal,Key Responsibility Area,關鍵責任區 DocType: Delivery Trip,Distance UOM,距離UOM apps/erpnext/erpnext/utilities/activation.py +127,"Student Batches help you track attendance, assessments and fees for students",學生批幫助您跟踪學生的出勤,評估和費用 DocType: Payment Entry,Total Allocated Amount,總撥款額 -apps/erpnext/erpnext/setup/doctype/company/company.py +163,Set default inventory account for perpetual inventory,設置永久庫存的默認庫存帳戶 +apps/erpnext/erpnext/setup/doctype/company/company.py +163,Set default inventory account for perpetual inventory,設置永久庫存的默認庫存科目 apps/erpnext/erpnext/stock/doctype/serial_no/serial_no.py +261,"Cannot deliver Serial No {0} of item {1} as it is reserved to \ fullfill Sales Order {2}",無法提供項目{1}的序列號{0},因為它保留在\ fullfill銷售訂單{2}中 DocType: Item Reorder,Material Request Type,材料需求類型 @@ -3532,7 +3532,7 @@ DocType: Task,% Progress,%進展 apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +130,Gain/Loss on Asset Disposal,在資產處置收益/損失 apps/erpnext/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.js +24,"Only the Student Applicant with the status ""Approved"" will be selected in the table below.",下表中將只選擇狀態為“已批准”的學生申請人。 DocType: Tax Withholding Category,Rates,價格 -apps/erpnext/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].py +121,Account number for account {0} is not available.
Please setup your Chart of Accounts correctly.,帳戶{0}的帳戶號碼不可用。
請正確設置您的會計科目表。 +apps/erpnext/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].py +121,Account number for account {0} is not available.
Please setup your Chart of Accounts correctly.,科目{0}的科目號碼不可用。
請正確設置您的會計科目表。 DocType: Task,Depends on Tasks,取決於任務 apps/erpnext/erpnext/config/selling.py +36,Manage Customer Group Tree.,管理客戶群組樹。 DocType: Normal Test Items,Result Value,結果值 @@ -3657,7 +3657,7 @@ Examples: DocType: Issue,Issue Type,發行類型 DocType: Attendance,Leave Type,休假類型 DocType: Purchase Invoice,Supplier Invoice Details,供應商發票明細 -apps/erpnext/erpnext/controllers/stock_controller.py +237,Expense / Difference account ({0}) must be a 'Profit or Loss' account,費用/差異帳戶({0})必須是一個'溢利或虧損的帳戶 +apps/erpnext/erpnext/controllers/stock_controller.py +237,Expense / Difference account ({0}) must be a 'Profit or Loss' account,費用/差異科目({0})必須是一個'收益或損失'的科目 DocType: Project,Copied From,複製自 DocType: Project,Copied From,複製自 apps/erpnext/erpnext/projects/doctype/timesheet/timesheet.py +265,Invoice already created for all billing hours,發票已在所有結算時間創建 @@ -3701,7 +3701,7 @@ DocType: Asset,In Maintenance,在維護中 DocType: Amazon MWS Settings,Click this button to pull your Sales Order data from Amazon MWS.,單擊此按鈕可從亞馬遜MWS中提取銷售訂單數據。 DocType: Purchase Invoice,Overdue,過期的 DocType: Account,Stock Received But Not Billed,庫存接收,但不付款 -apps/erpnext/erpnext/accounts/doctype/account/account.py +91,Root Account must be a group,根帳戶必須是一組 +apps/erpnext/erpnext/accounts/doctype/account/account.py +91,Root Account must be a group,Root 科目必須是群組科目 DocType: Drug Prescription,Drug Prescription,藥物處方 DocType: Loan,Repaid/Closed,償還/關閉 DocType: Item,Total Projected Qty,預計總數量 @@ -3737,7 +3737,7 @@ apps/erpnext/erpnext/stock/doctype/item/item.py +578,Item {0} does not exist,項 DocType: Sales Invoice,Customer Address,客戶地址 DocType: Loan,Loan Details,貸款詳情 apps/erpnext/erpnext/setup/setup_wizard/setup_wizard.py +62,Failed to setup post company fixtures,未能設置公司固定裝置 -DocType: Company,Default Inventory Account,默認庫存帳戶 +DocType: Company,Default Inventory Account,默認庫存科目 apps/erpnext/erpnext/accounts/doctype/share_transfer/share_transfer.py +193,The folio numbers are not matching,作品集編號不匹配 apps/erpnext/erpnext/accounts/doctype/payment_request/payment_request.py +304,Payment Request for {0},付款申請{0} DocType: Item Barcode,Barcode Type,條碼類型 @@ -3786,7 +3786,7 @@ DocType: Antibiotic,Healthcare Administrator,醫療管理員 apps/erpnext/erpnext/utilities/user_progress.py +47,Set a Target,設定目標 DocType: Dosage Strength,Dosage Strength,劑量強度 DocType: Healthcare Practitioner,Inpatient Visit Charge,住院訪問費用 -DocType: Account,Expense Account,費用帳戶 +DocType: Account,Expense Account,費用科目 apps/erpnext/erpnext/setup/setup_wizard/data/industry_type.py +49,Software,軟件 apps/erpnext/erpnext/setup/setup_wizard/operations/install_fixtures.py +155,Colour,顏色 DocType: Assessment Plan Criteria,Assessment Plan Criteria,評估計劃標準 @@ -3860,7 +3860,7 @@ DocType: Contract,Fulfilment Terms,履行條款 DocType: Sales Invoice,Time Sheet List,時間表列表 DocType: Employee,You can enter any date manually,您可以手動輸入任何日期 DocType: Healthcare Settings,Result Printed,結果打印 -DocType: Asset Category Account,Depreciation Expense Account,折舊費用帳戶 +DocType: Asset Category Account,Depreciation Expense Account,折舊費用科目 apps/erpnext/erpnext/setup/setup_wizard/operations/install_fixtures.py +191,Probationary Period,試用期 DocType: Purchase Taxes and Charges Template,Is Inter State,是國際 apps/erpnext/erpnext/config/hr.py +269,Shift Management,班次管理 @@ -3900,7 +3900,7 @@ apps/erpnext/erpnext/hr/utils.py +154,Future dates not allowed,未來的日期 apps/erpnext/erpnext/support/page/support_analytics/support_analytics.js +30,Select Fiscal Year,選擇財政年度 apps/erpnext/erpnext/selling/doctype/sales_order/sales_order.py +119,Expected Delivery Date should be after Sales Order Date,預計交貨日期應在銷售訂單日期之後 apps/erpnext/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py +43,Reorder Level,重新排序級別 -DocType: Company,Chart Of Accounts Template,圖表帳戶模板 +DocType: Company,Chart Of Accounts Template,會計科目模板 apps/erpnext/erpnext/assets/doctype/asset/asset.py +80,Update stock must be enable for the purchase invoice {0},必須為購買發票{0}啟用更新庫存 apps/erpnext/erpnext/stock/get_item_details.py +405,Item Price updated for {0} in Price List {1},項目價格更新{0}價格表{1} DocType: Salary Structure,Salary breakup based on Earning and Deduction.,工資分手基於盈利和演繹。 @@ -3957,7 +3957,7 @@ apps/erpnext/erpnext/accounts/doctype/cost_center/cost_center.py +40,Cost Center DocType: QuickBooks Migrator,Authorization URL,授權URL apps/erpnext/erpnext/accounts/doctype/payment_entry/payment_entry.py +376,Amount {0} {1} {2} {3},金額{0} {1} {2} {3} DocType: Account,Depreciation,折舊 -apps/erpnext/erpnext/accounts/doctype/share_transfer/share_transfer.py +103,The number of shares and the share numbers are inconsistent,股份數量和股票數量不一致 +apps/erpnext/erpnext/accounts/doctype/share_transfer/share_transfer.py +103,The number of shares and the share numbers are inconsistent,股份數量和庫存數量不一致 apps/erpnext/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py +50,Supplier(s),供應商(S) DocType: Employee Attendance Tool,Employee Attendance Tool,員工考勤工具 DocType: Guardian Student,Guardian Student,學生監護人 @@ -3981,8 +3981,8 @@ apps/erpnext/erpnext/manufacturing/doctype/production_planning_tool/production_p DocType: Restaurant Reservation,No of People,沒有人 apps/erpnext/erpnext/config/selling.py +164,Template of terms or contract.,模板條款或合同。 DocType: Bank Account,Address and Contact,地址和聯絡方式 -DocType: Cheque Print Template,Is Account Payable,為應付賬款 -apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +306,Stock cannot be updated against Purchase Receipt {0},股票不能對外購入庫單進行更新{0} +DocType: Cheque Print Template,Is Account Payable,為應付帳款 +apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +306,Stock cannot be updated against Purchase Receipt {0},庫存不能對外購入庫單進行更新{0} DocType: Support Settings,Auto close Issue after 7 days,7天之後自動關閉問題 apps/erpnext/erpnext/hr/doctype/leave_allocation/leave_allocation.py +85,"Leave cannot be allocated before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}",假,不是之前分配{0},因為休假餘額已經結轉轉發在未來的假期分配記錄{1} apps/erpnext/erpnext/accounts/party.py +350,Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s),註:由於/參考日期由{0}天超過了允許客戶的信用天數(S) @@ -4035,7 +4035,7 @@ apps/erpnext/erpnext/accounts/report/trial_balance/trial_balance.py +271,Closing apps/erpnext/erpnext/stock/doctype/serial_no/serial_no.py +284,Serial No {0} not in stock,序列號{0}無貨 apps/erpnext/erpnext/config/selling.py +169,Tax template for selling transactions.,稅務模板賣出的交易。 DocType: Sales Invoice,Write Off Outstanding Amount,核銷額(億元) -apps/erpnext/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py +27,Account {0} does not match with Company {1},帳戶{0}與公司{1}不符 +apps/erpnext/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py +27,Account {0} does not match with Company {1},科目{0}與公司{1}不符 DocType: Education Settings,Current Academic Year,當前學年 DocType: Education Settings,Current Academic Year,當前學年 DocType: Stock Settings,Default Stock UOM,預設庫存計量單位 @@ -4055,13 +4055,13 @@ apps/erpnext/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py +59 apps/erpnext/erpnext/controllers/accounts_controller.py +703,'Update Stock' cannot be checked for fixed asset sale,"""更新庫存"" 無法檢查固定資產銷售" DocType: Bank Reconciliation,Bank Reconciliation,銀行對帳 apps/erpnext/erpnext/templates/includes/footer/footer_extension.html +7,Get Updates,獲取更新 -apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +97,{0} {1}: Account {2} does not belong to Company {3},{0} {1}帳戶{2}不屬於公司{3} +apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +97,{0} {1}: Account {2} does not belong to Company {3},{0} {1}科目{2}不屬於公司{3} apps/erpnext/erpnext/stock/doctype/item/item.js +452,Select at least one value from each of the attributes.,從每個屬性中至少選擇一個值。 apps/erpnext/erpnext/buying/doctype/purchase_order/purchase_order.py +164,Material Request {0} is cancelled or stopped,材料需求{0}被取消或停止 apps/erpnext/erpnext/regional/report/eway_bill/eway_bill.py +219,Dispatch State,派遣國 apps/erpnext/erpnext/config/hr.py +399,Leave Management,離開管理 apps/erpnext/erpnext/stock/doctype/item/item_dashboard.py +17,Groups,組 -apps/erpnext/erpnext/accounts/report/general_ledger/general_ledger.py +51,Group by Account,以帳戶分群組 +apps/erpnext/erpnext/accounts/report/general_ledger/general_ledger.py +51,Group by Account,以科目分群組 DocType: Purchase Invoice,Hold Invoice,保留發票 apps/erpnext/erpnext/hr/doctype/employee_promotion/employee_promotion.js +37,Please select Employee,請選擇員工 apps/erpnext/erpnext/setup/setup_wizard/operations/install_fixtures.py +214,Lower Income,較低的收入 @@ -4069,7 +4069,7 @@ DocType: Restaurant Order Entry,Current Order,當前訂單 apps/erpnext/erpnext/assets/doctype/asset_movement/asset_movement.py +25,Number of serial nos and quantity must be the same,序列號和數量必須相同 apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py +275,Source and target warehouse cannot be same for row {0},列{0}的來源和目標倉庫不可相同 DocType: Account,Asset Received But Not Billed,已收到但未收費的資產 -apps/erpnext/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +244,"Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry",差異帳戶必須是資產/負債類型的帳戶,因為此庫存調整是一個開始分錄 +apps/erpnext/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +244,"Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry",差異科目必須是資產/負債類型的科目,因為此庫存調整是一個開帳分錄 apps/erpnext/erpnext/hr/doctype/loan/loan.py +116,Disbursed Amount cannot be greater than Loan Amount {0},支付額不能超過貸款金額較大的{0} apps/erpnext/erpnext/utilities/user_progress.py +176,Go to Programs,轉到程序 apps/erpnext/erpnext/hr/doctype/expense_claim/expense_claim.py +212,Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2},行{0}#分配的金額{1}不能大於無人認領的金額{2} @@ -4117,7 +4117,7 @@ apps/erpnext/erpnext/patches/v7_0/create_warehouse_nestedset.py +59,All Warehous apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +1303,No {0} found for Inter Company Transactions.,Inter公司沒有找到{0}。 DocType: Travel Itinerary,Rented Car,租車 apps/erpnext/erpnext/public/js/hub/components/profile_dialog.js +15,About your Company,關於貴公司 -apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +144,Credit To account must be a Balance Sheet account,信用帳戶必須是資產負債表科目 +apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +144,Credit To account must be a Balance Sheet account,貸方科目必須是資產負債表科目 DocType: Donor,Donor,捐贈者 DocType: Global Defaults,Disable In Words,禁用詞 apps/erpnext/erpnext/stock/doctype/item/item.py +74,Item Code is mandatory because Item is not automatically numbered,產品編號是強制性的,因為項目沒有自動編號 @@ -4136,7 +4136,7 @@ apps/erpnext/erpnext/accounts/doctype/payment_entry/payment_entry.py +95,Row #{0 apps/erpnext/erpnext/manufacturing/doctype/bom/bom.js +84,Browse BOM,瀏覽BOM apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +165,Secured Loans,抵押貸款 DocType: Purchase Invoice,Edit Posting Date and Time,編輯投稿時間 -apps/erpnext/erpnext/assets/doctype/asset/depreciation.py +106,Please set Depreciation related Accounts in Asset Category {0} or Company {1},請設置在資產類別{0}或公司折舊相關帳戶{1} +apps/erpnext/erpnext/assets/doctype/asset/depreciation.py +106,Please set Depreciation related Accounts in Asset Category {0} or Company {1},請設置在資產類別{0}或公司折舊相關科目{1} DocType: Lab Test Groups,Normal Range,普通範圍 DocType: Academic Term,Academic Year,學年 apps/erpnext/erpnext/stock/report/item_variant_details/item_variant_details.py +79,Available Selling,可用銷售 @@ -4145,7 +4145,7 @@ apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standar DocType: Purchase Invoice,N,ñ apps/erpnext/erpnext/education/report/assessment_plan_status/assessment_plan_status.py +175,Remaining,剩餘 DocType: Appraisal,Appraisal,評價 -DocType: Loan,Loan Account,貸款帳戶 +DocType: Loan,Loan Account,貸款科目 DocType: Purchase Invoice,GST Details,消費稅細節 apps/erpnext/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner_dashboard.py +6,This is based on transactions against this Healthcare Practitioner.,這是基於針對此醫療保健從業者的交易。 apps/erpnext/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +156,Email sent to supplier {0},電子郵件發送到供應商{0} @@ -4168,9 +4168,9 @@ apps/erpnext/erpnext/buying/doctype/request_for_quotation/request_for_quotation. apps/erpnext/erpnext/manufacturing/doctype/bom/bom.py +187,{0} not found for Item {1},找不到項目{1} {0} apps/erpnext/erpnext/utilities/user_progress.py +197,Go to Courses,去課程 DocType: Accounts Settings,Show Inclusive Tax In Print,在打印中顯示包含稅 -apps/erpnext/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +17,"Bank Account, From Date and To Date are Mandatory",銀行賬戶,從日期到日期是強制性的 +apps/erpnext/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +17,"Bank Account, From Date and To Date are Mandatory",銀行科目,從日期到日期是強制性的 apps/erpnext/erpnext/accounts/doctype/payment_request/payment_request.js +29,Message Sent,發送訊息 -apps/erpnext/erpnext/accounts/doctype/account/account.py +105,Account with child nodes cannot be set as ledger,帳戶與子節點不能被設置為分類帳 +apps/erpnext/erpnext/accounts/doctype/account/account.py +105,Account with child nodes cannot be set as ledger,科目與子節點不能被設置為分類帳 DocType: Sales Invoice,Rate at which Price list currency is converted to customer's base currency,價目表貨幣被換算成客戶基礎貨幣的匯率 DocType: Purchase Invoice Item,Net Amount (Company Currency),淨金額(公司貨幣) apps/erpnext/erpnext/hr/doctype/expense_claim/expense_claim.py +223,Total advance amount cannot be greater than total sanctioned amount,總預付金額不得超過全部認可金額 @@ -4178,7 +4178,7 @@ DocType: Salary Slip,Hour Rate,小時率 DocType: Stock Settings,Item Naming By,產品命名規則 apps/erpnext/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +46,Another Period Closing Entry {0} has been made after {1},另一個期末錄入{0}作出後{1} DocType: Work Order,Material Transferred for Manufacturing,物料轉倉用於製造 -apps/erpnext/erpnext/accounts/report/general_ledger/general_ledger.py +49,Account {0} does not exists,帳戶{0}不存在 +apps/erpnext/erpnext/accounts/report/general_ledger/general_ledger.py +49,Account {0} does not exists,科目{0}不存在 apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +1608,Select Loyalty Program,選擇忠誠度計劃 DocType: Project,Project Type,專案類型 apps/erpnext/erpnext/projects/doctype/task/task.py +157,Child Task exists for this Task. You can not delete this Task.,子任務存在這個任務。你不能刪除這個任務。 @@ -4199,7 +4199,7 @@ apps/erpnext/erpnext/accounts/doctype/shipping_rule/shipping_rule.py +101,Shippi apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +20,Cash In Hand,手頭現金 apps/erpnext/erpnext/selling/doctype/sales_order/sales_order.py +143,Delivery warehouse required for stock item {0},需要的庫存項目交割倉庫{0} DocType: Packing Slip,The gross weight of the package. Usually net weight + packaging material weight. (for print),包裹的總重量。通常為淨重+包裝材料的重量。 (用於列印) -DocType: Accounts Settings,Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,具有此角色的用戶可以設置凍結帳戶,並新增/修改對凍結帳戶的會計分錄 +DocType: Accounts Settings,Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,具有此角色的用戶可以設置凍結科目,並新增/修改對凍結科目的會計分錄 DocType: Vital Signs,Cuts,削減 DocType: Serial No,Is Cancelled,被註銷 DocType: Student Group,Group Based On,基於組 @@ -4218,7 +4218,7 @@ apps/erpnext/erpnext/education/doctype/student_attendance_tool/student_attendanc ,Issued Items Against Work Order,針對工單發布物品 ,BOM Stock Calculated,BOM庫存計算 DocType: Vehicle Log,Invoice Ref,發票編號 -DocType: Company,Default Income Account,預設之收入帳戶 +DocType: Company,Default Income Account,預設收入科目 apps/erpnext/erpnext/accounts/report/balance_sheet/balance_sheet.py +39,Unclosed Fiscal Years Profit / Loss (Credit),未關閉的財年利潤/損失(信用) DocType: Sales Invoice,Time Sheets,考勤表 DocType: Healthcare Service Unit Type,Change In Item,更改項目 @@ -4258,7 +4258,7 @@ DocType: Soil Texture,Silt Composition (%),粉塵成分(%) DocType: Journal Entry,Remark,備註 DocType: Healthcare Settings,Avoid Confirmation,避免確認 DocType: Purchase Receipt Item,Rate and Amount,率及金額 -apps/erpnext/erpnext/accounts/doctype/payment_entry/payment_entry.py +177,Account Type for {0} must be {1},帳戶類型為{0}必須{1} +apps/erpnext/erpnext/accounts/doctype/payment_entry/payment_entry.py +177,Account Type for {0} must be {1},科目類型為{0}必須{1} apps/erpnext/erpnext/config/hr.py +34,Leaves and Holiday,休假及假日 DocType: Education Settings,Current Academic Term,當前學術期限 DocType: Education Settings,Current Academic Term,當前學術期限 @@ -4272,7 +4272,7 @@ DocType: Purchase Invoice Item,Landed Cost Voucher Amount,到岸成本憑證金 apps/erpnext/erpnext/config/accounts.py +19,Bills raised by Suppliers.,由供應商提出的帳單。 DocType: POS Profile,Write Off Account,核銷帳戶 DocType: Patient Appointment,Get prescribed procedures,獲取規定的程序 -DocType: Sales Invoice,Redemption Account,贖回賬戶 +DocType: Sales Invoice,Redemption Account,贖回科目 DocType: Purchase Invoice Item,Discount Amount,折扣金額 DocType: Purchase Invoice,Return Against Purchase Invoice,回到對採購發票 DocType: Item,Warranty Period (in days),保修期限(天數) @@ -4372,7 +4372,7 @@ apps/erpnext/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py +3 apps/erpnext/erpnext/hr/report/salary_register/salary_register.py +47,Salary Slip ID,工資單編號 apps/erpnext/erpnext/hr/doctype/employee/employee.py +135,Date Of Retirement must be greater than Date of Joining,日期退休必須大於加入的日期 apps/erpnext/erpnext/stock/doctype/item/item.js +70,Multiple Variants,多種變體 -DocType: Sales Invoice,Against Income Account,對收入帳戶 +DocType: Sales Invoice,Against Income Account,對收入科目 DocType: Subscription,Trial Period Start Date,試用期開始日期 apps/erpnext/erpnext/buying/doctype/purchase_order/purchase_order.py +106,Item {0}: Ordered qty {1} cannot be less than minimum order qty {2} (defined in Item).,項目{0}:有序數量{1}不能低於最低訂貨量{2}(項中定義)。 DocType: Certification Application,Certified,認證 @@ -4402,7 +4402,7 @@ DocType: Asset,Journal Entry for Scrap,日記帳分錄報廢 apps/erpnext/erpnext/selling/doctype/installation_note/installation_note.py +83,Please pull items from Delivery Note,請送貨單拉項目 apps/erpnext/erpnext/manufacturing/doctype/work_order/work_order.py +242,Row {0}: select the workstation against the operation {1},行{0}:根據操作{1}選擇工作站 apps/erpnext/erpnext/accounts/utils.py +496,Journal Entries {0} are un-linked,日記條目{0}都是非聯 -apps/erpnext/erpnext/accounts/utils.py +825,{0} Number {1} already used in account {2},已在{2}帳戶中使用的{0}號碼{1} +apps/erpnext/erpnext/accounts/utils.py +825,{0} Number {1} already used in account {2},已在{2}科目中使用的{0}號碼{1} apps/erpnext/erpnext/config/crm.py +92,"Record of all communications of type email, phone, chat, visit, etc.",類型電子郵件,電話,聊天,訪問等所有通信記錄 DocType: Supplier Scorecard Scoring Standing,Supplier Scorecard Scoring Standing,供應商記分卡 DocType: Manufacturer,Manufacturers used in Items,在項目中使用製造商 @@ -4428,7 +4428,7 @@ DocType: Salary Component,"If selected, the value specified or calculated in thi DocType: Asset Settings,Number of Days in Fiscal Year,會計年度的天數 ,Stock Ledger,庫存總帳 apps/erpnext/erpnext/templates/includes/cart/cart_items.html +29,Rate: {0},價格:{0} -DocType: Company,Exchange Gain / Loss Account,兌換收益/損失帳戶 +DocType: Company,Exchange Gain / Loss Account,匯兌損益科目 DocType: Amazon MWS Settings,MWS Credentials,MWS憑證 apps/erpnext/erpnext/config/hr.py +7,Employee and Attendance,員工考勤 apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py +123,Purpose must be one of {0},目的必須是一個{0} @@ -4450,7 +4450,7 @@ DocType: Cash Flow Mapper,Section Name,部分名稱 apps/erpnext/erpnext/stock/report/stock_balance/stock_balance.py +92,Reorder Qty,再訂購數量 apps/erpnext/erpnext/assets/doctype/asset/asset.py +292,Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1},折舊行{0}:使用壽命後的預期值必須大於或等於{1} apps/erpnext/erpnext/hr/doctype/job_opening/job_opening.py +55,Current Job Openings,當前職位空缺 -DocType: Company,Stock Adjustment Account,庫存調整帳戶 +DocType: Company,Stock Adjustment Account,庫存調整科目 apps/erpnext/erpnext/public/js/payment/pos_payment.html +17,Write Off,註銷項款 DocType: Healthcare Service Unit,Allow Overlap,允許重疊 DocType: Employee,"System User (login) ID. If set, it will become default for all HR forms.",系統用戶(登錄)的標識。如果設定,這將成為的所有人力資源的預設形式。 @@ -4497,7 +4497,7 @@ DocType: Purchase Order,Order Confirmation Date,訂單確認日期 apps/erpnext/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js +47,Make Maintenance Visit,使維護訪問 DocType: Employee Transfer,Employee Transfer Details,員工轉移詳情 apps/erpnext/erpnext/selling/doctype/customer/customer.py +263,Please contact to the user who have Sales Master Manager {0} role,請聯絡,誰擁有碩士學位的銷售經理{0}角色的用戶 -DocType: Company,Default Cash Account,預設的現金帳戶 +DocType: Company,Default Cash Account,預設的現金科目 apps/erpnext/erpnext/config/accounts.py +40,Company (not Customer or Supplier) master.,公司(不是客戶或供應商)的主人。 apps/erpnext/erpnext/education/doctype/student/student_dashboard.py +6,This is based on the attendance of this Student,這是基於這名學生出席 apps/erpnext/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.js +179,No Students in,沒有學生 @@ -4515,12 +4515,12 @@ DocType: Opportunity,Opportunity Type,機會型 DocType: Asset Movement,To Employee,給員工 DocType: Employee Transfer,New Company,新公司 apps/erpnext/erpnext/setup/doctype/company/delete_company_transactions.py +19,Transactions can only be deleted by the creator of the Company,交易只能由公司的創建者被刪除 -apps/erpnext/erpnext/accounts/general_ledger.py +21,Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction.,不正確的數字總帳條目中找到。你可能會在交易中選擇了錯誤的帳戶。 +apps/erpnext/erpnext/accounts/general_ledger.py +21,Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction.,不正確的數字總帳條目中找到。你可能會在交易中選擇了錯誤的科目。 DocType: Employee,Prefered Contact Email,首選聯繫郵箱 DocType: Cheque Print Template,Cheque Width,支票寬度 DocType: Selling Settings,Validate Selling Price for Item against Purchase Rate or Valuation Rate,驗證售價反對預訂價或估價RATE項目 DocType: Fee Schedule,Fee Schedule,收費表 -DocType: Company,Create Chart Of Accounts Based On,創建圖表的帳戶根據 +DocType: Company,Create Chart Of Accounts Based On,基於會計科目表創建 apps/erpnext/erpnext/hr/doctype/employee/employee.py +129,Date of Birth cannot be greater than today.,出生日期不能大於今天。 ,Stock Ageing,存貨帳齡分析表 DocType: Travel Request,"Partially Sponsored, Require Partial Funding",部分贊助,需要部分資金 @@ -4541,7 +4541,7 @@ DocType: Purchase Order,Customer Contact Email,客戶聯絡電子郵件 DocType: Warranty Claim,Item and Warranty Details,項目和保修細節 DocType: Chapter,Chapter Members,章節成員 DocType: Sales Team,Contribution (%),貢獻(%) -apps/erpnext/erpnext/controllers/accounts_controller.py +143,Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified,注:付款項將不會被創建因為“現金或銀行帳戶”未指定 +apps/erpnext/erpnext/controllers/accounts_controller.py +143,Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified,注:付款項將不會被創建因為“現金或銀行科目”未指定 apps/erpnext/erpnext/projects/doctype/project/project.py +79,Project {0} already exists,項目{0}已經存在 DocType: Clinical Procedure,Nursing User,護理用戶 DocType: Employee Benefit Application,Payroll Period,工資期 @@ -4549,7 +4549,7 @@ DocType: Plant Analysis,Plant Analysis Criterias,植物分析標準 apps/erpnext/erpnext/stock/doctype/serial_no/serial_no.py +239,Serial No {0} does not belong to Batch {1},序列號{0}不屬於批次{1} apps/erpnext/erpnext/setup/setup_wizard/operations/install_fixtures.py +197,Responsibilities,職責 apps/erpnext/erpnext/selling/doctype/quotation/quotation.py +125,Validity period of this quotation has ended.,此報價的有效期已經結束。 -DocType: Expense Claim Account,Expense Claim Account,報銷賬戶 +DocType: Expense Claim Account,Expense Claim Account,報銷科目 DocType: Account,Capital Work in Progress,資本工作正在進行中 DocType: Accounts Settings,Allow Stale Exchange Rates,允許陳舊的匯率 DocType: Sales Person,Sales Person Name,銷售人員的姓名 @@ -4565,7 +4565,7 @@ apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.ht apps/erpnext/erpnext/projects/doctype/task/task.py +57,Progress % for a task cannot be more than 100.,為任務進度百分比不能超過100個。 DocType: Stock Reconciliation Item,Before reconciliation,調整前 DocType: Purchase Invoice,Taxes and Charges Added (Company Currency),稅收和收費上架(公司貨幣) -apps/erpnext/erpnext/stock/doctype/item/item.py +507,Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable,商品稅行{0}必須有帳戶類型稅或收入或支出或課稅的 +apps/erpnext/erpnext/stock/doctype/item/item.py +507,Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable,商品稅行{0}必須有科目類型為"稅" 或 "收入" 或 "支出" 或 "課稅的" DocType: Sales Order,Partly Billed,天色帳單 apps/erpnext/erpnext/assets/doctype/asset/asset.py +54,Item {0} must be a Fixed Asset Item,項{0}必須是固定資產項目 apps/erpnext/erpnext/stock/doctype/item/item.js +427,Make Variants,變種 @@ -4577,13 +4577,13 @@ apps/erpnext/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_re apps/erpnext/erpnext/setup/doctype/company/company.js +109,Please re-type company name to confirm,請確認重新輸入公司名稱 apps/erpnext/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +50,Total Outstanding Amt,總街貨量金額 DocType: Journal Entry,Printing Settings,列印設定 -DocType: Employee Advance,Advance Account,預付帳戶 +DocType: Employee Advance,Advance Account,預付款科目 DocType: Job Offer,Job Offer Terms,招聘條款 DocType: Sales Invoice,Include Payment (POS),包括支付(POS) apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +324,Total Debit must be equal to Total Credit. The difference is {0},借方總額必須等於貸方總額。差額為{0} apps/erpnext/erpnext/setup/setup_wizard/data/industry_type.py +11,Automotive,汽車 DocType: Vehicle,Insurance Company,保險公司 -DocType: Asset Category Account,Fixed Asset Account,固定資產帳戶 +DocType: Asset Category Account,Fixed Asset Account,固定資產科目 apps/erpnext/erpnext/hr/doctype/salary_structure/salary_structure.js +442,Variable,變量 apps/erpnext/erpnext/selling/doctype/installation_note/installation_note.js +47,From Delivery Note,從送貨單 DocType: Chapter,Members,會員 @@ -4595,14 +4595,14 @@ apps/erpnext/erpnext/public/js/pos/pos_bill_item.html +12,In Stock: ,有現貨 DocType: Notification Control,Custom Message,自定義訊息 apps/erpnext/erpnext/setup/setup_wizard/data/industry_type.py +33,Investment Banking,投資銀行業務 DocType: Purchase Invoice,input,輸入 -apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +107,Cash or Bank Account is mandatory for making payment entry,製作付款分錄時,現金或銀行帳戶是強制性輸入的欄位。 +apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +107,Cash or Bank Account is mandatory for making payment entry,製作付款分錄時,現金或銀行科目是強制性輸入的欄位。 DocType: Loyalty Program,Multiple Tier Program,多層計劃 apps/erpnext/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py +54,Student Address,學生地址 apps/erpnext/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py +54,Student Address,學生地址 DocType: Purchase Invoice,Price List Exchange Rate,價目表匯率 apps/erpnext/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py +27,All Supplier Groups,所有供應商組織 DocType: Employee Boarding Activity,Required for Employee Creation,員工創建需要 -apps/erpnext/erpnext/accounts/doctype/account/account.py +214,Account Number {0} already used in account {1},已在帳戶{1}中使用的帳號{0} +apps/erpnext/erpnext/accounts/doctype/account/account.py +214,Account Number {0} already used in account {1},已在科目{1}中使用的帳號{0} DocType: Hotel Room Reservation,Booked,預訂 DocType: Detected Disease,Tasks Created,創建的任務 DocType: Purchase Invoice Item,Rate,單價 @@ -4840,7 +4840,7 @@ apps/erpnext/erpnext/setup/doctype/supplier_group/supplier_group.js +5,There is apps/erpnext/erpnext/selling/page/point_of_sale/point_of_sale.js +542,Form View,表單視圖 DocType: HR Settings,Expense Approver Mandatory In Expense Claim,費用審批人必須在費用索賠中 apps/erpnext/erpnext/setup/doctype/email_digest/email_digest.py +124,Summary for this month and pending activities,本月和待活動總結 -apps/erpnext/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +91,Please set Unrealized Exchange Gain/Loss Account in Company {0},請在公司{0}中設置未實現的匯兌收益/損失帳戶 +apps/erpnext/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +91,Please set Unrealized Exchange Gain/Loss Account in Company {0},請在公司{0}中設置未實現的匯兌收益/損失科目 apps/erpnext/erpnext/utilities/user_progress.py +248,"Add users to your organization, other than yourself.",將用戶添加到您的組織,而不是您自己。 DocType: Customer Group,Customer Group Name,客戶群組名稱 apps/erpnext/erpnext/public/js/pos/pos.html +109,No Customers yet!,還沒有客戶! @@ -4856,7 +4856,7 @@ DocType: Healthcare Practitioner,Phone (R),電話(R) apps/erpnext/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.js +104,Time slots added,添加時隙 DocType: Item,Attributes,屬性 apps/erpnext/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js +36,Enable Template,啟用模板 -apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +256,Please enter Write Off Account,請輸入核銷帳戶 +apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +256,Please enter Write Off Account,請輸入核銷科目 apps/erpnext/erpnext/selling/report/inactive_customers/inactive_customers.py +71,Last Order Date,最後訂購日期 DocType: Salary Component,Is Payable,應付 DocType: Inpatient Record,B Negative,B負面 @@ -4865,7 +4865,7 @@ DocType: Amazon MWS Settings,US,我們 DocType: Holiday List,Add Weekly Holidays,添加每週假期 DocType: Staffing Plan Detail,Vacancies,職位空缺 DocType: Hotel Room,Hotel Room,旅館房間 -apps/erpnext/erpnext/accounts/doctype/budget/budget.py +57,Account {0} does not belongs to company {1},帳戶{0}不屬於公司{1} +apps/erpnext/erpnext/accounts/doctype/budget/budget.py +57,Account {0} does not belongs to company {1},科目{0}不屬於公司{1} DocType: Leave Type,Rounding,四捨五入 apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +994,Serial Numbers in row {0} does not match with Delivery Note,行{0}中的序列號與交貨單不匹配 DocType: Employee Benefit Application,Dispensed Amount (Pro-rated),分配金額(按比例分配) @@ -4896,7 +4896,7 @@ DocType: Patient,Alcohol Current Use,酒精當前使用 DocType: Employee Tax Exemption Proof Submission,House Rent Payment Amount,房屋租金付款金額 DocType: Student Admission Program,Student Admission Program,學生入學計劃 DocType: Employee Tax Exemption Sub Category,Tax Exemption Category,免稅類別 -DocType: Payment Entry,Account Paid To,賬戶付至 +DocType: Payment Entry,Account Paid To,科目付至 DocType: Subscription Settings,Grace Period,寬限期 DocType: Item Alternative,Alternative Item Name,替代項目名稱 apps/erpnext/erpnext/selling/doctype/product_bundle/product_bundle.py +24,Parent Item {0} must not be a Stock Item,父項{0}不能是庫存產品 @@ -4905,7 +4905,7 @@ apps/erpnext/erpnext/config/selling.py +57,All Products or Services.,所有的 DocType: Email Digest,Open Quotations,打開報價單 DocType: Expense Claim,More Details,更多詳情 DocType: Supplier Quotation,Supplier Address,供應商地址 -apps/erpnext/erpnext/accounts/doctype/budget/budget.py +168,{0} Budget for Account {1} against {2} {3} is {4}. It will exceed by {5},{0}預算帳戶{1}對{2} {3}是{4}。這將超過{5} +apps/erpnext/erpnext/accounts/doctype/budget/budget.py +168,{0} Budget for Account {1} against {2} {3} is {4}. It will exceed by {5},{0}預算科目{1}對{2} {3}是{4}。這將超過{5} apps/erpnext/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +37,Out Qty,輸出數量 apps/erpnext/erpnext/buying/doctype/supplier/supplier.py +49,Series is mandatory,系列是強制性的 apps/erpnext/erpnext/setup/setup_wizard/data/industry_type.py +28,Financial Services,金融服務 @@ -4991,7 +4991,7 @@ DocType: Job Offer,Awaiting Response,正在等待回應 DocType: Support Search Source,Link Options,鏈接選項 apps/erpnext/erpnext/selling/page/point_of_sale/point_of_sale.js +1557,Total Amount {0},總金額{0} apps/erpnext/erpnext/controllers/item_variant.py +323,Invalid attribute {0} {1},無效的屬性{0} {1} -DocType: Supplier,Mention if non-standard payable account,如果非標準應付賬款提到 +DocType: Supplier,Mention if non-standard payable account,如果非標準應付帳款提到 apps/erpnext/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.py +25,Please select the assessment group other than 'All Assessment Groups',請選擇“所有評估組”以外的評估組 apps/erpnext/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +67,Row {0}: Cost center is required for an item {1},行{0}:項目{1}需要費用中心 DocType: Training Event Employee,Optional,可選的 @@ -5047,7 +5047,7 @@ apps/erpnext/erpnext/selling/report/inactive_customers/inactive_customers.py +67 DocType: Item Group,HTML / Banner that will show on the top of product list.,HTML/橫幅,將顯示在產品列表的頂部。 DocType: Shipping Rule,Specify conditions to calculate shipping amount,指定條件來計算運費金額 DocType: Program Enrollment,Institute's Bus,學院的巴士 -DocType: Accounts Settings,Role Allowed to Set Frozen Accounts & Edit Frozen Entries,允許設定凍結帳戶和編輯凍結分錄的角色 +DocType: Accounts Settings,Role Allowed to Set Frozen Accounts & Edit Frozen Entries,允許設定凍結科目和編輯凍結分錄的角色 DocType: Supplier Scorecard Scoring Variable,Path,路徑 apps/erpnext/erpnext/accounts/doctype/cost_center/cost_center.py +30,Cannot convert Cost Center to ledger as it has child nodes,不能成本中心轉換為總賬,因為它有子節點 DocType: Production Plan,Total Planned Qty,總計劃數量 @@ -5055,7 +5055,7 @@ apps/erpnext/erpnext/stock/report/stock_balance/stock_balance.py +83,Opening Val DocType: Salary Component,Formula,式 apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py +59,Serial #,序列號 DocType: Lab Test Template,Lab Test Template,實驗室測試模板 -apps/erpnext/erpnext/setup/doctype/company/company.py +196,Sales Account,銷售帳戶 +apps/erpnext/erpnext/setup/doctype/company/company.py +196,Sales Account,銷售科目 DocType: Purchase Invoice Item,Total Weight,總重量 apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +101,Commission on Sales,銷售佣金 DocType: Job Offer Term,Value / Description,值/說明 @@ -5078,7 +5078,7 @@ DocType: Cash Flow Mapping,Select Maximum Of 1,選擇最多1個 apps/erpnext/erpnext/stock/doctype/packing_slip/packing_slip.js +84,Invalid quantity specified for item {0}. Quantity should be greater than 0.,為項目指定了無效的數量{0} 。量應大於0 。 DocType: Company,Default Employee Advance Account,默認員工高級帳戶 apps/erpnext/erpnext/selling/page/point_of_sale/point_of_sale.js +1181,Search Item (Ctrl + i),搜索項目(Ctrl + i) -apps/erpnext/erpnext/accounts/doctype/account/account.py +171,Account with existing transaction can not be deleted,帳戶與現有的交易不能被刪除 +apps/erpnext/erpnext/accounts/doctype/account/account.py +171,Account with existing transaction can not be deleted,科目與現有的交易不能被刪除 DocType: Vehicle,Last Carbon Check,最後檢查炭 apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +109,Legal Expenses,法律費用 apps/erpnext/erpnext/public/js/utils/serial_no_batch_selector.js +147,Please select quantity on row ,請選擇行數量 @@ -5100,7 +5100,7 @@ apps/erpnext/erpnext/controllers/accounts_controller.py +907,Account: {0} with c DocType: Bank Statement Transaction Settings Item,Bank Data,銀行數據 DocType: Purchase Receipt Item,Sample Quantity,樣品數量 DocType: Manufacturing Settings,"Update BOM cost automatically via Scheduler, based on latest valuation rate / price list rate / last purchase rate of raw materials.",根據最新的估值/價格清單率/最近的原材料採購率,通過計劃程序自動更新BOM成本。 -apps/erpnext/erpnext/accounts/doctype/account/account.py +57,Account {0}: Parent account {1} does not belong to company: {2},帳戶{0}:父帳戶{1}不屬於公司:{2} +apps/erpnext/erpnext/accounts/doctype/account/account.py +57,Account {0}: Parent account {1} does not belong to company: {2},科目{0}:上層科目{1}不屬於公司:{2} apps/erpnext/erpnext/setup/doctype/company/company.js +126,Successfully deleted all transactions related to this company!,成功刪除與該公司相關的所有交易! apps/erpnext/erpnext/accounts/report/accounts_payable/accounts_payable.js +22,As on Date,隨著對日 DocType: Program Enrollment,Enrollment Date,報名日期 @@ -5126,7 +5126,7 @@ DocType: Academic Year,Academic Year Name,學年名稱 apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +1182,{0} not allowed to transact with {1}. Please change the Company.,不允許{0}與{1}進行交易。請更改公司。 DocType: Sales Partner,Contact Desc,聯絡倒序 DocType: Email Digest,Send regular summary reports via Email.,使用電子郵件發送定期匯總報告。 -apps/erpnext/erpnext/hr/doctype/expense_claim/expense_claim.py +289,Please set default account in Expense Claim Type {0},請報銷類型設置默認帳戶{0} +apps/erpnext/erpnext/hr/doctype/expense_claim/expense_claim.py +289,Please set default account in Expense Claim Type {0},請報銷類型設置默認科目{0} apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html +11,Available Leaves,可用的葉子 DocType: Assessment Result,Student Name,學生姓名 DocType: Hub Tracked Item,Item Manager,項目經理 @@ -5169,17 +5169,17 @@ apps/erpnext/erpnext/accounts/doctype/budget/budget.py +155,Accumulated Monthly, apps/erpnext/erpnext/controllers/accounts_controller.py +864,{0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}.,{0}是強制性的。也許外幣兌換記錄為{1}到{2}尚未建立。 apps/erpnext/erpnext/hr/doctype/staffing_plan/staffing_plan.py +51,Staffing Plan {0} already exist for designation {1},已存在人員配置計劃{0}以用於指定{1} apps/erpnext/erpnext/accounts/doctype/tax_rule/tax_rule.py +46,Tax Template is mandatory.,稅務模板是強制性的。 -apps/erpnext/erpnext/accounts/doctype/account/account.py +51,Account {0}: Parent account {1} does not exist,帳戶{0}:父帳戶{1}不存在 +apps/erpnext/erpnext/accounts/doctype/account/account.py +51,Account {0}: Parent account {1} does not exist,科目{0}:上層科目{1}不存在 DocType: POS Closing Voucher,Period Start Date,期間開始日期 DocType: Purchase Invoice Item,Price List Rate (Company Currency),價格列表費率(公司貨幣) DocType: Products Settings,Products Settings,產品設置 -,Item Price Stock,項目價格股票 +,Item Price Stock,項目價格庫存 apps/erpnext/erpnext/config/accounts.py +550,To make Customer based incentive schemes.,制定基於客戶的激勵計劃。 DocType: Lab Prescription,Test Created,測試創建 DocType: Healthcare Settings,Custom Signature in Print,自定義簽名打印 DocType: Account,Temporary,臨時 apps/erpnext/erpnext/accounts/report/accounts_receivable/accounts_receivable.html +127,Customer LPO No.,客戶LPO號 -DocType: Amazon MWS Settings,Market Place Account Group,市場賬戶組 +DocType: Amazon MWS Settings,Market Place Account Group,市場科目組 apps/erpnext/erpnext/accounts/doctype/payment_order/payment_order.js +14,Make Payment Entries,付款條目 DocType: Program,Courses,培訓班 DocType: Monthly Distribution Percentage,Percentage Allocation,百分比分配 @@ -5215,7 +5215,7 @@ DocType: Selling Settings,Each Transaction,每筆交易 apps/erpnext/erpnext/stock/doctype/item/item.py +523,Barcode {0} already used in Item {1},條碼{0}已經用在項目{1} apps/erpnext/erpnext/config/selling.py +86,Rules for adding shipping costs.,增加運輸成本的規則。 apps/erpnext/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +72,Varaiance ,Varaiance -DocType: Item,Opening Stock,打開股票 +DocType: Item,Opening Stock,打開庫存 apps/erpnext/erpnext/support/doctype/warranty_claim/warranty_claim.py +20,Customer is required,客戶是必需的 DocType: Lab Test,Result Date,結果日期 apps/erpnext/erpnext/accounts/report/accounts_receivable/accounts_receivable.html +129,PDC/LC Date,PDC / LC日期 @@ -5250,7 +5250,7 @@ apps/erpnext/erpnext/templates/includes/product_list.js +42,No products found., apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +396,{0} against Sales Invoice {1},{0}針對銷售發票{1} DocType: Antibiotic,Laboratory User,實驗室用戶 DocType: Request for Quotation Item,Project Name,專案名稱 -DocType: Customer,Mention if non-standard receivable account,提到如果不規範應收賬款 +DocType: Customer,Mention if non-standard receivable account,提到如果不規範應收帳款 apps/erpnext/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py +63,Please add the remaining benefits {0} to any of the existing component,請將其餘好處{0}添加到任何現有組件 DocType: Journal Entry Account,If Income or Expense,如果收入或支出 DocType: Bank Statement Transaction Entry,Matching Invoices,匹配發票 @@ -5260,7 +5260,7 @@ apps/erpnext/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +6 apps/erpnext/erpnext/config/learn.py +229,Human Resource,人力資源 DocType: Payment Reconciliation Payment,Payment Reconciliation Payment,付款方式付款對賬 DocType: Disease,Treatment Task,治療任務 -DocType: Payment Order Reference,Bank Account Details,銀行賬戶明細 +DocType: Payment Order Reference,Bank Account Details,銀行科目明細 DocType: Purchase Order Item,Blanket Order,總訂單 apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +39,Tax Assets,所得稅資產 apps/erpnext/erpnext/manufacturing/doctype/production_order/production_order.py +631,Production Order has been {0},生產訂單已經{0} @@ -5322,7 +5322,7 @@ apps/erpnext/erpnext/manufacturing/doctype/work_order/work_order_calendar.js +36 ,Employee Information,僱員資料 apps/erpnext/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +242,Healthcare Practitioner not available on {0},醫療從業者在{0}上不可用 DocType: Stock Entry Detail,Additional Cost,額外費用 -apps/erpnext/erpnext/accounts/report/general_ledger/general_ledger.py +57,"Can not filter based on Voucher No, if grouped by Voucher",是冷凍的帳戶。要禁止該帳戶創建/編輯事務,你需要角色 +apps/erpnext/erpnext/accounts/report/general_ledger/general_ledger.py +57,"Can not filter based on Voucher No, if grouped by Voucher",是凍結的帳戶。要禁止該帳戶創建/編輯事務,你需要有指定的身份 apps/erpnext/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +975,Make Supplier Quotation,讓供應商報價 DocType: Quality Inspection,Incoming,來 apps/erpnext/erpnext/setup/doctype/company/company.js +90,Default tax templates for sales and purchase are created.,銷售和採購的默認稅收模板被創建。 @@ -5340,7 +5340,7 @@ apps/erpnext/erpnext/setup/doctype/email_digest/email_digest.py +120,This Week's apps/erpnext/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py +24,In Stock Qty,庫存數量 ,Daily Work Summary Replies,日常工作總結回复 DocType: Delivery Trip,Calculate Estimated Arrival Times,計算預計到達時間 -apps/erpnext/erpnext/accounts/general_ledger.py +113,Account: {0} can only be updated via Stock Transactions,帳號:{0}只能通過股票的交易進行更新 +apps/erpnext/erpnext/accounts/general_ledger.py +113,Account: {0} can only be updated via Stock Transactions,帳號:{0}只能通過庫存的交易進行更新 DocType: Student Group Creation Tool,Get Courses,獲取課程 DocType: Shopify Settings,Webhooks,網絡掛接 DocType: Bank Account,Party,黨 @@ -5376,7 +5376,7 @@ apps/erpnext/erpnext/selling/doctype/sales_order/sales_order.py +87,Same item ha DocType: Department,Leave Block List,休假區塊清單 DocType: Purchase Invoice,Tax ID,稅號 apps/erpnext/erpnext/stock/doctype/serial_no/serial_no.py +198,Item {0} is not setup for Serial Nos. Column must be blank,項目{0}不是設定為序列號,此列必須為空白 -DocType: Accounts Settings,Accounts Settings,帳戶設定 +DocType: Accounts Settings,Accounts Settings,會計設定 DocType: Loyalty Program,Customer Territory,客戶地區 DocType: Email Digest,Sales Orders to Deliver,要交付的銷售訂單 apps/erpnext/erpnext/accounts/doctype/account/account_tree.js +28,"Number of new Account, it will be included in the account name as a prefix",新帳號的數量,將作為前綴包含在帳號名稱中 @@ -5388,7 +5388,7 @@ apps/erpnext/erpnext/hr/utils.py +152,To date can not be less than from date,迄 DocType: Opportunity,To Discuss,為了討論 apps/erpnext/erpnext/stock/stock_ledger.py +382,{0} units of {1} needed in {2} to complete this transaction.,{0}單位{1}在{2}完成此交易所需。 DocType: Support Settings,Forum URL,論壇URL -apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +75,Temporary Accounts,臨時帳戶 +apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +75,Temporary Accounts,臨時科目 apps/erpnext/erpnext/assets/doctype/asset_movement/asset_movement.py +40,Source Location is required for the asset {0},源位置對資產{0}是必需的 DocType: BOM Explosion Item,BOM Explosion Item,BOM展開項目 DocType: Shareholder,Contact List,聯繫人列表 @@ -5426,7 +5426,7 @@ apps/erpnext/erpnext/education/doctype/course/course.py +20,Total Weightage of a DocType: Purchase Order Item,Last Purchase Rate,最後預訂價 DocType: Account,Asset,財富 DocType: Project Task,Task ID,任務ID -apps/erpnext/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +84,Stock cannot exist for Item {0} since has variants,股票可以為項目不存在{0},因為有變種 +apps/erpnext/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +84,Stock cannot exist for Item {0} since has variants,庫存可以為項目不存在{0},因為有變種 DocType: Healthcare Practitioner,Mobile,移動 ,Sales Person-wise Transaction Summary,銷售人員相關的交易匯總 DocType: Training Event,Contact Number,聯繫電話 @@ -5444,7 +5444,7 @@ DocType: Employee,Reports to,隸屬於 DocType: Payment Entry,Paid Amount,支付的金額 apps/erpnext/erpnext/utilities/user_progress.py +158,Explore Sales Cycle,探索銷售週期 DocType: Assessment Plan,Supervisor,監 -apps/erpnext/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +934,Retention Stock Entry,保留股票入場 +apps/erpnext/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +934,Retention Stock Entry,保留庫存入場 ,Available Stock for Packing Items,可用庫存包裝項目 DocType: Item Variant,Item Variant,項目變 ,Work Order Stock Report,工單庫存報表 @@ -5454,7 +5454,7 @@ apps/erpnext/erpnext/education/doctype/instructor/instructor.js +45,As Superviso DocType: Leave Policy Detail,Leave Policy Detail,退出政策細節 DocType: BOM Scrap Item,BOM Scrap Item,BOM項目廢料 apps/erpnext/erpnext/accounts/page/pos/pos.js +906,Submitted orders can not be deleted,提交的訂單不能被刪除 -apps/erpnext/erpnext/accounts/doctype/account/account.py +121,"Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'",帳戶餘額已歸為借方帳戶,不允許設為信用帳戶 +apps/erpnext/erpnext/accounts/doctype/account/account.py +121,"Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'",科目餘額已歸為借方科目,不允許設為貸方 apps/erpnext/erpnext/setup/setup_wizard/operations/install_fixtures.py +391,Quality Management,品質管理 apps/erpnext/erpnext/assets/doctype/asset/asset.py +52,Item {0} has been disabled,項{0}已被禁用 DocType: Project,Total Billable Amount (via Timesheets),總計費用金額(通過時間表) @@ -5484,15 +5484,15 @@ apps/erpnext/erpnext/manufacturing/doctype/workstation/workstation.py +36,Row #{ DocType: Purchase Invoice Item,Allow Zero Valuation Rate,允許零估值 DocType: Purchase Invoice Item,Allow Zero Valuation Rate,允許零估值 DocType: Training Event Employee,Invited,邀請 -apps/erpnext/erpnext/config/accounts.py +276,Setup Gateway accounts.,設置網關帳戶。 +apps/erpnext/erpnext/config/accounts.py +276,Setup Gateway accounts.,設置閘道科目。 DocType: Employee,Employment Type,就業類型 apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +43,Fixed Assets,固定資產 DocType: Payment Entry,Set Exchange Gain / Loss,設置兌換收益/損失 ,GST Purchase Register,消費稅購買登記冊 ,Cash Flow,現金周轉 apps/erpnext/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py +25,Combined invoice portion must equal 100%,合併發票部分必須等於100% -DocType: Item Default,Default Expense Account,預設費用帳戶 -DocType: GST Account,CGST Account,CGST賬戶 +DocType: Item Default,Default Expense Account,預設費用科目 +DocType: GST Account,CGST Account,CGST科目 apps/erpnext/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py +53,Student Email ID,學生的電子郵件ID DocType: POS Closing Voucher Invoices,POS Closing Voucher Invoices,POS關閉憑證發票 DocType: Tax Rule,Sales Tax Template,銷售稅模板 @@ -5523,7 +5523,7 @@ The package **Item** will have ""Is Stock Item"" as ""No"" and ""Is Sales Item"" For Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item. -Note: BOM = Bill of Materials",聚合組** **項目到另一個項目** **的。如果你是捆綁了一定**項目你保持股票的包裝**項目的**,而不是聚集**項這是一個有用的**到一個包和**。包** **項目將有“是股票項目”為“否”和“是銷售項目”為“是”。例如:如果你是銷售筆記本電腦和背包分開,並有一個特殊的價格,如果客戶購買兩個,那麼筆記本電腦+背包將是一個新的產品包項目。注:物料BOM =比爾 +Note: BOM = Bill of Materials",聚合組** **項目到另一個項目** **的。如果你是捆綁了一定**項目你保持庫存的包裝**項目的**,而不是聚集**項這是一個有用的**到一個包和**。包** **項目將有“是庫存項目”為“否”和“是銷售項目”為“是”。例如:如果你是銷售筆記本電腦和背包分開,並有一個特殊的價格,如果客戶購買兩個,那麼筆記本電腦+背包將是一個新的產品包項目。注:物料BOM =比爾 apps/erpnext/erpnext/selling/doctype/installation_note/installation_note.py +42,Serial No is mandatory for Item {0},項目{0}的序列號是強制性的 DocType: Item Variant Attribute,Attribute,屬性 DocType: Staffing Plan Detail,Current Count,當前計數 @@ -5564,7 +5564,7 @@ apps/erpnext/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +75,Max disco apps/erpnext/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +191,Net Asset value as on,淨資產值作為 DocType: Crop,Produce,生產 DocType: Hotel Settings,Default Taxes and Charges,默認稅費 -DocType: Account,Receivable,應收賬款 +DocType: Account,Receivable,應收帳款 apps/erpnext/erpnext/selling/doctype/sales_order/sales_order.py +325,Row #{0}: Not allowed to change Supplier as Purchase Order already exists,行#{0}:不能更改供應商的採購訂單已經存在 DocType: Stock Entry,Material Consumption for Manufacture,材料消耗製造 DocType: Item Alternative,Alternative Item Code,替代項目代碼 @@ -5603,7 +5603,7 @@ DocType: Asset,Booked Fixed Asset,預訂的固定資產 apps/erpnext/erpnext/accounts/report/trial_balance/trial_balance.py +49,To Date should be within the Fiscal Year. Assuming To Date = {0},日期應該是在財政年度內。假設終止日期= {0} DocType: Employee,"Here you can maintain height, weight, allergies, medical concerns etc",在這裡,你可以保持身高,體重,過敏,醫療問題等 DocType: Leave Block List,Applies to Company,適用於公司 -apps/erpnext/erpnext/manufacturing/doctype/production_order/production_order.py +222,Cannot cancel because submitted Stock Entry {0} exists,不能取消,因為提交股票輸入{0}存在 +apps/erpnext/erpnext/manufacturing/doctype/production_order/production_order.py +222,Cannot cancel because submitted Stock Entry {0} exists,不能取消,因為提交庫存輸入{0}存在 DocType: BOM Update Tool,Update latest price in all BOMs,更新所有BOM的最新價格 apps/erpnext/erpnext/healthcare/doctype/patient/patient.js +24,Medical Record,醫療記錄 DocType: Vehicle,Vehicle,車輛 @@ -5613,13 +5613,13 @@ apps/erpnext/erpnext/hr/doctype/training_result/training_result.py +15,{0} must DocType: POS Profile,Item Groups,項目組 DocType: Sales Order Item,For Production,對於生產 DocType: Payment Request,payment_url,payment_url -DocType: Exchange Rate Revaluation Account,Balance In Account Currency,賬戶貨幣餘額 -apps/erpnext/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +188,Please add a Temporary Opening account in Chart of Accounts,請在會計科目表中添加一個臨時開戶賬戶 +DocType: Exchange Rate Revaluation Account,Balance In Account Currency,科目貨幣餘額 +apps/erpnext/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +188,Please add a Temporary Opening account in Chart of Accounts,請在會計科目表中添加一個臨時開戶科目 DocType: Customer,Customer Primary Contact,客戶主要聯繫人 DocType: Project Task,View Task,查看任務 apps/erpnext/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py +22,Opp/Lead %,Opp / Lead% apps/erpnext/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py +22,Opp/Lead %,Opp / Lead% -DocType: Bank Guarantee,Bank Account Info,銀行賬戶信息 +DocType: Bank Guarantee,Bank Account Info,銀行科目信息 DocType: Bank Guarantee,Bank Guarantee Type,銀行擔保類型 DocType: Payment Schedule,Invoice Portion,發票部分 ,Asset Depreciations and Balances,資產折舊和平衡 @@ -5701,7 +5701,7 @@ DocType: Certification Application,Yet to appear,尚未出現 DocType: Delivery Stop,Email Sent To,電子郵件發送給 DocType: Job Card Item,Job Card Item,工作卡項目 DocType: Accounts Settings,Allow Cost Center In Entry of Balance Sheet Account,允許成本中心輸入資產負債表科目 -apps/erpnext/erpnext/accounts/doctype/account/account.js +102,Merge with Existing Account,與現有帳戶合併 +apps/erpnext/erpnext/accounts/doctype/account/account.js +102,Merge with Existing Account,與現有科目合併 apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py +992,All items have already been transferred for this Work Order.,所有項目已經為此工作單轉移。 DocType: Appraisal,"Any other remarks, noteworthy effort that should go in the records.",任何其他言論,值得一提的努力,應該在記錄中。 DocType: Asset Maintenance,Manufacturing User,製造業用戶 @@ -5739,14 +5739,14 @@ apps/erpnext/erpnext/manufacturing/doctype/work_order/work_order.py +203,{0} ({1 DocType: Certification Application,Name of Applicant,申請人名稱 apps/erpnext/erpnext/config/manufacturing.py +27,Time Sheet for manufacturing.,時間表製造。 apps/erpnext/erpnext/templates/pages/cart.html +37,Subtotal,小計 -apps/erpnext/erpnext/stock/doctype/item/item.py +731,Cannot change Variant properties after stock transaction. You will have to make a new Item to do this.,股票交易後不能更改Variant屬性。你將不得不做一個新的項目來做到這一點。 +apps/erpnext/erpnext/stock/doctype/item/item.py +731,Cannot change Variant properties after stock transaction. You will have to make a new Item to do this.,庫存交易後不能更改Variant屬性。你將不得不做一個新的項目來做到這一點。 apps/erpnext/erpnext/config/integrations.py +18,GoCardless SEPA Mandate,GoCardless SEPA授權 DocType: Healthcare Practitioner,Charges,收費 DocType: Production Plan,Get Items For Work Order,獲取工作訂單的物品 DocType: Salary Detail,Default Amount,預設數量 apps/erpnext/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +96,Warehouse not found in the system,倉庫系統中未找到 DocType: Quality Inspection Reading,Quality Inspection Reading,質量檢驗閱讀 -apps/erpnext/erpnext/stock/doctype/stock_settings/stock_settings.py +26,`Freeze Stocks Older Than` should be smaller than %d days.,`凍結股票早於`應該是少於%d天。 +apps/erpnext/erpnext/stock/doctype/stock_settings/stock_settings.py +26,`Freeze Stocks Older Than` should be smaller than %d days.,`凍結庫存早於`應該是少於%d天。 DocType: Tax Rule,Purchase Tax Template,購置稅模板 apps/erpnext/erpnext/utilities/user_progress.py +48,Set a sales goal you'd like to achieve for your company.,為您的公司設定您想要實現的銷售目標。 apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +1553,Healthcare Services,醫療服務 @@ -5781,7 +5781,7 @@ apps/erpnext/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js +5 DocType: Warranty Claim,Resolved By,議決 apps/erpnext/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +32,Schedule Discharge,附表卸貨 apps/erpnext/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py +42,Cheques and Deposits incorrectly cleared,支票及存款不正確清除 -apps/erpnext/erpnext/accounts/doctype/account/account.py +53,Account {0}: You can not assign itself as parent account,帳戶{0}:你不能指定自己為父帳戶 +apps/erpnext/erpnext/accounts/doctype/account/account.py +53,Account {0}: You can not assign itself as parent account,科目{0}:你不能指定自己為上層科目 DocType: Purchase Invoice Item,Price List Rate,價格列表費率 apps/erpnext/erpnext/utilities/activation.py +72,Create customer quotes,創建客戶報價 apps/erpnext/erpnext/public/js/controllers/transaction.js +885,Service Stop Date cannot be after Service End Date,服務停止日期不能在服務結束日期之後 @@ -5817,7 +5817,7 @@ DocType: Daily Work Summary Settings,"Emails will be sent to all Active Employee DocType: Employee Leave Approver,Employee Leave Approver,員工請假審批 apps/erpnext/erpnext/stock/doctype/item/item.py +541,Row {0}: An Reorder entry already exists for this warehouse {1},行{0}:一個重新排序條目已存在這個倉庫{1} apps/erpnext/erpnext/crm/doctype/opportunity/opportunity.py +99,"Cannot declare as lost, because Quotation has been made.",不能聲明為丟失,因為報價已經取得進展。 -apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +68,CWIP Account,CWIP賬戶 +apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +68,CWIP Account,CWIP科目 apps/erpnext/erpnext/hr/doctype/training_event/training_event.js +16,Training Feedback,培訓反饋 apps/erpnext/erpnext/config/accounts.py +184,Tax Withholding rates to be applied on transactions.,稅收預扣稅率適用於交易。 DocType: Supplier Scorecard Criteria,Supplier Scorecard Criteria,供應商記分卡標準 @@ -5850,7 +5850,7 @@ DocType: Agriculture Analysis Criteria,Agriculture User,農業用戶 apps/erpnext/erpnext/stock/stock_ledger.py +386,{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.,{0} {1}在需要{2}在{3} {4}:{5}來完成這一交易單位。 DocType: Fee Schedule,Student Category,學生組 DocType: Announcement,Student,學生 -apps/erpnext/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +98,Stock quantity to start procedure is not available in the warehouse. Do you want to record a Stock Transfer,倉庫中不提供開始操作的庫存數量。你想記錄股票轉移嗎? +apps/erpnext/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +98,Stock quantity to start procedure is not available in the warehouse. Do you want to record a Stock Transfer,倉庫中不提供開始操作的庫存數量。你想記錄庫存轉移嗎? DocType: Shipping Rule,Shipping Rule Type,運輸規則類型 apps/erpnext/erpnext/utilities/user_progress.py +239,Go to Rooms,去房間 apps/erpnext/erpnext/hr/doctype/payroll_entry/payroll_entry.js +259,"Company, Payment Account, From Date and To Date is mandatory",公司,付款帳戶,從日期和日期是強制性的 @@ -5871,7 +5871,7 @@ DocType: Purchase Receipt Item,Received and Accepted,收貨及允收 DocType: Staffing Plan,Staffing Plan Details,人員配置計劃詳情 ,Serial No Service Contract Expiry,序號服務合同到期 DocType: Employee Health Insurance,Employee Health Insurance,員工健康保險 -apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +331,You cannot credit and debit same account at the same time,你無法將貸方與借方在同一時間記在同一帳戶 +apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +331,You cannot credit and debit same account at the same time,你無法將貸方與借方在同一時間記在同一科目 DocType: Vital Signs,Adults' pulse rate is anywhere between 50 and 80 beats per minute.,成年人的脈率在每分鐘50到80次之間。 DocType: Naming Series,Help HTML,HTML幫助 DocType: Student Group Creation Tool,Student Group Creation Tool,學生組創建工具 @@ -5954,7 +5954,7 @@ DocType: Shopping Cart Settings,Checkout Settings,結帳設定 DocType: Student Attendance,Present,現在 apps/erpnext/erpnext/stock/doctype/packing_slip/packing_slip.py +37,Delivery Note {0} must not be submitted,送貨單{0}不能提交 DocType: Notification Control,Sales Invoice Message,銷售發票訊息 -apps/erpnext/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +27,Closing Account {0} must be of type Liability / Equity,關閉帳戶{0}的類型必須是負債/權益 +apps/erpnext/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +27,Closing Account {0} must be of type Liability / Equity,關閉科目{0}的類型必須是負債/權益 apps/erpnext/erpnext/hr/doctype/salary_slip/salary_slip.py +407,Salary Slip of employee {0} already created for time sheet {1},員工的工資單{0}已為時間表創建{1} DocType: Production Plan Item,Ordered Qty,訂購數量 apps/erpnext/erpnext/stock/doctype/item/item.py +822,Item {0} is disabled,項目{0}無效 @@ -5997,11 +5997,11 @@ DocType: Subscription Plan,Subscription Plan,訂閱計劃 DocType: Employee External Work History,Salary,薪水 DocType: Serial No,Delivery Document Type,交付文件類型 DocType: Item Variant Settings,Do not update variants on save,不要在保存時更新變體 -DocType: Email Digest,Receivables,應收賬款 +DocType: Email Digest,Receivables,應收帳款 DocType: Lead Source,Lead Source,主導來源 DocType: Customer,Additional information regarding the customer.,對於客戶的其他訊息。 DocType: Quality Inspection Reading,Reading 5,閱讀5 -apps/erpnext/erpnext/accounts/doctype/payment_entry/payment_entry.py +237,"{0} {1} is associated with {2}, but Party Account is {3}","{0} {1} 與 {2} 關聯, 但當事方帳戶為 {3}" +apps/erpnext/erpnext/accounts/doctype/payment_entry/payment_entry.py +237,"{0} {1} is associated with {2}, but Party Account is {3}","{0} {1} 與 {2} 關聯, 但當事方科目為 {3}" DocType: Bank Statement Settings Item,Bank Header,銀行標題 apps/erpnext/erpnext/healthcare/doctype/sample_collection/sample_collection.js +7,View Lab Tests,查看實驗室測試 DocType: Hub Users,Hub Users,Hub用戶 @@ -6060,12 +6060,12 @@ apps/erpnext/erpnext/education/doctype/program_enrollment_tool/program_enrollmen DocType: Fees,Student Details,學生細節 DocType: Purchase Invoice Item,Stock Qty,庫存數量 DocType: Purchase Invoice Item,Stock Qty,庫存數量 -DocType: QuickBooks Migrator,Default Shipping Account,默認運輸帳戶 +DocType: QuickBooks Migrator,Default Shipping Account,默認運輸科目 DocType: Loan,Repayment Period in Months,在月還款期 apps/erpnext/erpnext/templates/includes/footer/footer_extension.html +26,Error: Not a valid id?,錯誤:沒有有效的身份證? DocType: Naming Series,Update Series Number,更新序列號 DocType: Account,Equity,公平 -apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +79,{0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry,{0} {1}:“損益”帳戶類型{2}不允許進入開 +apps/erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py +79,{0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry,{0} {1}:“損益”科目類型{2}不允許進入開 DocType: Job Offer,Printing Details,印刷詳情 DocType: Task,Closing Date,截止日期 DocType: Sales Order Item,Produced Quantity,生產的產品數量 @@ -6075,14 +6075,14 @@ DocType: Employee Tax Exemption Category,Max Amount,最大金額 DocType: Journal Entry,Total Amount Currency,總金額幣種 apps/erpnext/erpnext/stock/report/bom_search/bom_search.js +38,Search Sub Assemblies,搜索子組件 apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +200,Item Code required at Row No {0},於列{0}需要產品編號 -DocType: GST Account,SGST Account,SGST賬戶 +DocType: GST Account,SGST Account,SGST科目 apps/erpnext/erpnext/utilities/user_progress.py +154,Go to Items,轉到項目 DocType: Sales Partner,Partner Type,合作夥伴類型 apps/erpnext/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +72,Actual,實際 DocType: Restaurant Menu,Restaurant Manager,餐廳經理 DocType: Authorization Rule,Customerwise Discount,Customerwise折扣 apps/erpnext/erpnext/config/projects.py +46,Timesheet for tasks.,時間表的任務。 -DocType: Purchase Invoice,Against Expense Account,對費用帳戶 +DocType: Purchase Invoice,Against Expense Account,對費用科目 apps/erpnext/erpnext/stock/doctype/delivery_note/delivery_note.py +287,Installation Note {0} has already been submitted,安裝注意{0}已提交 DocType: Bank Reconciliation,Get Payment Entries,獲取付款項 DocType: Quotation Item,Against Docname,對Docname @@ -6098,7 +6098,7 @@ DocType: Training Event,Employee Emails,員工電子郵件 apps/erpnext/erpnext/setup/doctype/naming_series/naming_series.py +67,Series Updated,系列更新 apps/erpnext/erpnext/accounts/doctype/account/account.py +166,Report Type is mandatory,報告類型是強制性的 DocType: Item,Serial Number Series,序列號系列 -apps/erpnext/erpnext/buying/utils.py +68,Warehouse is mandatory for stock Item {0} in row {1},倉庫是強制性的股票項目{0}行{1} +apps/erpnext/erpnext/buying/utils.py +68,Warehouse is mandatory for stock Item {0} in row {1},倉庫是強制性的庫存項目{0}行{1} apps/erpnext/erpnext/setup/setup_wizard/data/industry_type.py +45,Retail & Wholesale,零售及批發 DocType: Issue,First Responded On,首先作出回應 DocType: Website Item Group,Cross Listing of Item in multiple groups,在多組項目的交叉上市 @@ -6146,7 +6146,7 @@ DocType: Restaurant Reservation,Waitlisted,輪候 DocType: Employee Tax Exemption Declaration Category,Exemption Category,豁免類別 apps/erpnext/erpnext/accounts/doctype/account/account.py +131,Currency can not be changed after making entries using some other currency,貨幣不能使用其他貨幣進行輸入後更改 DocType: Vehicle Service,Clutch Plate,離合器壓盤 -DocType: Company,Round Off Account,四捨五入賬戶 +DocType: Company,Round Off Account,四捨五入科目 apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +100,Administrative Expenses,行政開支 apps/erpnext/erpnext/setup/setup_wizard/data/industry_type.py +18,Consulting,諮詢 DocType: Subscription Plan,Based on price list,基於價格表 @@ -6157,7 +6157,7 @@ DocType: Purchase Invoice,Contact Email,聯絡電郵 apps/erpnext/erpnext/education/doctype/fee_schedule/fee_schedule_list.js +11,Fee Creation Pending,費用創作待定 DocType: Appraisal Goal,Score Earned,得分 DocType: Asset Category,Asset Category Name,資產類別名稱 -apps/erpnext/erpnext/setup/doctype/territory/territory.js +13,This is a root territory and cannot be edited.,集團或Ledger ,借方或貸方,是特等帳戶 +apps/erpnext/erpnext/setup/doctype/territory/territory.js +13,This is a root territory and cannot be edited.,集團或Ledger ,借方或貸方,是特等科目 apps/erpnext/erpnext/setup/doctype/sales_person/sales_person_tree.js +5,New Sales Person Name,新銷售人員的姓名 DocType: Packing Slip,Gross Weight UOM,毛重計量單位 DocType: Employee Transfer,Create New Employee Id,創建新的員工ID @@ -6170,12 +6170,12 @@ DocType: Bin,Reserved Qty for Production,預留數量生產 DocType: Student Group Creation Tool,Leave unchecked if you don't want to consider batch while making course based groups. ,如果您不想在製作基於課程的組時考慮批量,請不要選中。 DocType: Student Group Creation Tool,Leave unchecked if you don't want to consider batch while making course based groups. ,如果您不想在製作基於課程的組時考慮批量,請不要選中。 DocType: Asset,Frequency of Depreciation (Months),折舊率(月) -apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.js +542,Credit Account,信用賬戶 +apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.js +542,Credit Account,信用科目 DocType: Landed Cost Item,Landed Cost Item,到岸成本項目 apps/erpnext/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +58,Show zero values,顯示零值 DocType: BOM,Quantity of item obtained after manufacturing / repacking from given quantities of raw materials,製造/從原材料數量給予重新包裝後獲得的項目數量 DocType: Lab Test,Test Group,測試組 -DocType: Payment Reconciliation,Receivable / Payable Account,應收/應付賬款 +DocType: Payment Reconciliation,Receivable / Payable Account,應收/應付帳款 DocType: Delivery Note Item,Against Sales Order Item,對銷售訂單項目 DocType: Company,Company Logo,公司標誌 apps/erpnext/erpnext/stock/doctype/item/item.py +787,Please specify Attribute Value for attribute {0},請指定屬性值的屬性{0} @@ -6199,10 +6199,10 @@ apps/erpnext/erpnext/stock/doctype/item/item.js +29,Balance,餘額 apps/erpnext/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +66,Please select the Company,請選擇公司 DocType: Room,Seating Capacity,座位數 DocType: Lab Test Groups,Lab Test Groups,實驗室測試組 -apps/erpnext/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +151,Party Type and Party is mandatory for {0} account,{0}帳戶必須使用派對類型和派對 +apps/erpnext/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +151,Party Type and Party is mandatory for {0} account,{0}科目的參與方以及類型為必填 DocType: Project,Total Expense Claim (via Expense Claims),總費用報銷(通過費用報銷) DocType: GST Settings,GST Summary,消費稅總結 -apps/erpnext/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py +16,Please enable default incoming account before creating Daily Work Summary Group,請在創建日常工作摘要組之前啟用默認傳入帳戶 +apps/erpnext/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py +16,Please enable default incoming account before creating Daily Work Summary Group,請在創建日常工作摘要組之前啟用默認傳入科目 DocType: Assessment Result,Total Score,總得分 DocType: Crop Cycle,ISO 8601 standard,ISO 8601標準 DocType: Journal Entry,Debit Note,繳費單 @@ -6222,7 +6222,7 @@ DocType: Manufacturing Settings,Default Finished Goods Warehouse,預設成品倉 apps/erpnext/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +380,Please select Patient,請選擇患者 apps/erpnext/erpnext/accounts/report/gross_profit/gross_profit.py +74,Sales Person,銷售人員 DocType: Hotel Room Package,Amenities,設施 -DocType: QuickBooks Migrator,Undeposited Funds Account,未存入資金賬戶 +DocType: QuickBooks Migrator,Undeposited Funds Account,未存入資金科目 apps/erpnext/erpnext/config/accounts.py +201,Budget and Cost Center,預算和成本中心 apps/erpnext/erpnext/accounts/doctype/pos_profile/pos_profile.py +65,Multiple default mode of payment is not allowed,不允許多種默認付款方式 DocType: Sales Invoice,Loyalty Points Redemption,忠誠積分兌換 @@ -6230,7 +6230,7 @@ DocType: Sales Invoice,Loyalty Points Redemption,忠誠積分兌換 DocType: Lead,Blog Subscriber,網誌訂閱者 DocType: Guardian,Alternate Number,備用號碼 apps/erpnext/erpnext/config/setup.py +83,Create rules to restrict transactions based on values.,創建規則來限制基於價值的交易。 -DocType: Cash Flow Mapping Accounts,Cash Flow Mapping Accounts,現金流量映射賬戶 +DocType: Cash Flow Mapping Accounts,Cash Flow Mapping Accounts,現金流量映射科目 apps/erpnext/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py +49, Group Roll No,組卷號 DocType: Batch,Manufacturing Date,生產日期 apps/erpnext/erpnext/education/doctype/fee_schedule/fee_schedule_list.js +9,Fee Creation Failed,費用創作失敗 @@ -6283,7 +6283,7 @@ DocType: Fiscal Year,Year Start Date,年結開始日期 DocType: Additional Salary,Employee Name,員工姓名 DocType: Restaurant Order Entry Item,Restaurant Order Entry Item,餐廳訂單錄入項目 DocType: Purchase Invoice,Rounded Total (Company Currency),整數總計(公司貨幣) -apps/erpnext/erpnext/accounts/doctype/account/account.py +103,Cannot covert to Group because Account Type is selected.,不能隱蔽到組,因為帳戶類型選擇的。 +apps/erpnext/erpnext/accounts/doctype/account/account.py +103,Cannot covert to Group because Account Type is selected.,不能轉換到群組科目,因為科目類型選擇的。 apps/erpnext/erpnext/selling/doctype/sales_order/sales_order.py +276,{0} {1} has been modified. Please refresh.,{0} {1} 已修改。請更新。 DocType: Leave Block List,Stop users from making Leave Applications on following days.,停止用戶在下面日期提出休假申請。 apps/erpnext/erpnext/accounts/doctype/loyalty_program/loyalty_program.js +24,"If unlimited expiry for the Loyalty Points, keep the Expiry Duration empty or 0.",如果忠誠度積分無限期到期,請將到期時間保持為空或0。 @@ -6312,7 +6312,7 @@ DocType: Company,Basic Component,基本組件 apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +578,Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2},行無{0}:金額不能大於金額之前對報銷{1}。待審核金額為{2} DocType: Patient Service Unit,Medical Administrator,醫療管理員 DocType: Assessment Plan,Schedule,時間表 -DocType: Account,Parent Account,父帳戶 +DocType: Account,Parent Account,上層科目 DocType: Quality Inspection Reading,Reading 3,閱讀3 DocType: Stock Entry,Source Warehouse Address,來源倉庫地址 DocType: GL Entry,Voucher Type,憑證類型 @@ -6356,7 +6356,7 @@ apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +249,Row {0 DocType: Employee Promotion,Employee Promotion,員工晉升 DocType: Maintenance Team Member,Maintenance Team Member,維護團隊成員 apps/erpnext/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.html +16,Course Code: ,課程編號: -apps/erpnext/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +241,Please enter Expense Account,請輸入您的費用帳戶 +apps/erpnext/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +241,Please enter Expense Account,請輸入您的費用科目 DocType: Account,Stock,庫存 apps/erpnext/erpnext/accounts/doctype/payment_entry/payment_entry.js +1128,"Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry",行#{0}:參考文件類型必須是採購訂單之一,購買發票或日記帳分錄 DocType: Employee,Current Address,當前地址 @@ -6372,7 +6372,7 @@ DocType: Sales Order,Track this Sales Order against any Project,跟踪對任何 DocType: Bank Statement Transaction Entry,Bank Statement Transaction Entry,銀行對賬單交易分錄 DocType: Sales Invoice Item,Discount and Margin,折扣和保證金 DocType: Lab Test,Prescription,處方 -DocType: Company,Default Deferred Revenue Account,默認遞延收入賬戶 +DocType: Company,Default Deferred Revenue Account,默認遞延收入科目 DocType: Project,Second Email,第二封郵件 DocType: Budget,Action if Annual Budget Exceeded on Actual,年度預算超出實際的行動 DocType: Pricing Rule,Min Qty,最小數量 @@ -6394,7 +6394,7 @@ apps/erpnext/erpnext/config/manufacturing.py +18,Generate Material Requests (MRP apps/erpnext/erpnext/accounts/doctype/pos_profile/pos_profile.py +62,Set default mode of payment,設置默認付款方式 DocType: BOM,With Operations,加入作業 DocType: Support Search Source,Post Route Key List,發布路由密鑰列表 -apps/erpnext/erpnext/accounts/party.py +288,Accounting entries have already been made in currency {0} for company {1}. Please select a receivable or payable account with currency {0}.,會計分錄已取得貨幣{0}為公司{1}。請選擇一個應收或應付賬戶幣種{0}。 +apps/erpnext/erpnext/accounts/party.py +288,Accounting entries have already been made in currency {0} for company {1}. Please select a receivable or payable account with currency {0}.,會計分錄已取得貨幣{0}為公司{1}。請選擇一個應收或應付科目幣種{0}。 DocType: Asset,Is Existing Asset,是對現有資產 DocType: Salary Component,Statistical Component,統計組成部分 DocType: Salary Component,Statistical Component,統計組成部分 @@ -6482,7 +6482,7 @@ apps/erpnext/erpnext/public/js/hub/components/item_publish_dialog.js +3,Edit Pub DocType: Packing Slip,Package Weight Details,包裝重量詳情 DocType: Leave Type,Is Compensatory,是有補償的 DocType: Restaurant Reservation,Reservation Time,預訂時間 -DocType: Payment Gateway Account,Payment Gateway Account,網路支付閘道帳戶 +DocType: Payment Gateway Account,Payment Gateway Account,網路支付閘道科目 DocType: Shopping Cart Settings,After payment completion redirect user to selected page.,支付完成後重定向用戶選擇的頁面。 DocType: Company,Existing Company,現有的公司 DocType: Healthcare Settings,Result Emailed,電子郵件結果 @@ -6507,7 +6507,7 @@ DocType: Terms and Conditions,Terms and Conditions Help,條款和條件幫助 ,Item-wise Purchase Register,項目明智的購買登記 DocType: Loyalty Point Entry,Expiry Date,到期時間 DocType: Healthcare Settings,Employee name and designation in print,員工姓名和印刷品名稱 -,accounts-browser,賬戶瀏覽器 +,accounts-browser,科目瀏覽器 apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +381,Please select Category first,請先選擇分類 apps/erpnext/erpnext/config/projects.py +13,Project master.,專案主持。 apps/erpnext/erpnext/controllers/status_updater.py +215,"To allow over-billing or over-ordering, update ""Allowance"" in Stock Settings or the Item.",要允許對賬單或過度訂貨,庫存設置或更新項目“津貼”。 @@ -6526,11 +6526,11 @@ apps/erpnext/erpnext/patches/v11_0/add_default_dispatch_notification_template.py apps/erpnext/erpnext/controllers/accounts_controller.py +693,Row #{0}: Posting Date must be same as purchase date {1} of asset {2},行#{0}:過帳日期必須是相同的購買日期{1}資產的{2} DocType: Program Enrollment,Check this if the Student is residing at the Institute's Hostel.,如果學生住在學院的旅館,請檢查。 apps/erpnext/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py +125,Please enter Sales Orders in the above table,請在上表中輸入銷售訂單 -,Stock Summary,股票摘要 +,Stock Summary,庫存摘要 apps/erpnext/erpnext/config/assets.py +62,Transfer an asset from one warehouse to another,從一個倉庫轉移資產到另一 DocType: Employee Benefit Application,Remaining Benefits (Yearly),剩餘福利(每年) apps/erpnext/erpnext/stock/doctype/material_request/material_request.js +873,Bill of Materials,材料清單 -apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +137,Row {0}: Party Type and Party is required for Receivable / Payable account {1},行{0}:黨的類型和黨的需要應收/應付帳戶{1} +apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +137,Row {0}: Party Type and Party is required for Receivable / Payable account {1},行{0}:參與方類型和參與方需要應收/應付科目{1} DocType: Employee,Leave Policy,離開政策 apps/erpnext/erpnext/buying/doctype/purchase_order/purchase_order.js +865,Update Items,更新項目 apps/erpnext/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py +94,Ref Date,參考日期 @@ -6542,7 +6542,7 @@ DocType: GL Entry,Is Opening,是開幕 DocType: Department,Expense Approvers,費用審批人 apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py +228,Row {0}: Debit entry can not be linked with a {1},行{0}:借方條目不能與{1}連接 DocType: Journal Entry,Subscription Section,認購科 -apps/erpnext/erpnext/accounts/doctype/account/account.py +238,Account {0} does not exist,帳戶{0}不存在 +apps/erpnext/erpnext/accounts/doctype/account/account.py +238,Account {0} does not exist,科目{0}不存在 DocType: Training Event,Training Program,培訓計劃 DocType: Account,Cash,現金 DocType: Employee,Short biography for website and other publications.,網站和其他出版物的短的傳記。 From f322c608cf7acbb8c5928f151876a2e5ddc13595 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Mon, 31 Oct 2022 12:41:37 +0530 Subject: [PATCH 0431/1047] fix: for asset's purchase_date, if bill_date is set, use that instead of posting_date --- erpnext/assets/doctype/asset/asset.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 5512d4159d..7e54219740 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -432,7 +432,11 @@ frappe.ui.form.on('Asset', { set_values_from_purchase_doc: function(frm, doctype, purchase_doc) { frm.set_value('company', purchase_doc.company); - frm.set_value('purchase_date', purchase_doc.posting_date); + if (purchase_doc.bill_date) { + frm.set_value('purchase_date', purchase_doc.bill_date); + } else { + frm.set_value('purchase_date', purchase_doc.posting_date); + } const item = purchase_doc.items.find(item => item.item_code === frm.doc.item_code); if (!item) { doctype_field = frappe.scrub(doctype) From 45ededbed52e61910476ac4df33b62d16ea89395 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 27 Oct 2022 18:16:30 +0530 Subject: [PATCH 0432/1047] fix: duplicate custom fields for inventory dimension --- .../inventory_dimension/inventory_dimension.py | 14 ++++++++++---- .../test_inventory_dimension.py | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py index 9e8c10b394..7b99b0097b 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py @@ -121,18 +121,24 @@ class InventoryDimension(Document): if self.apply_to_all_doctypes: for doctype in get_inventory_documents(): - custom_fields.setdefault(doctype[0], dimension_fields) - else: + if not field_exists(doctype[0], self.source_fieldname): + custom_fields.setdefault(doctype[0], dimension_fields) + elif not field_exists(self.document_type, self.source_fieldname): custom_fields.setdefault(self.document_type, dimension_fields) if not frappe.db.get_value( "Custom Field", {"dt": "Stock Ledger Entry", "fieldname": self.target_fieldname} - ): + ) and not field_exists("Stock Ledger Entry", self.target_fieldname): dimension_field = dimension_fields[1] dimension_field["fieldname"] = self.target_fieldname custom_fields["Stock Ledger Entry"] = dimension_field - create_custom_fields(custom_fields) + if custom_fields: + create_custom_fields(custom_fields) + + +def field_exists(doctype, fieldname) -> str or None: + return frappe.db.get_value("DocField", {"parent": doctype, "fieldname": fieldname}, "name") @frappe.whitelist() diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py index 19ddc449f0..52b3deb3f0 100644 --- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py @@ -191,6 +191,21 @@ class TestInventoryDimension(FrappeTestCase): self.assertEqual(sle_rack, "Rack 1") + def test_check_standard_dimensions(self): + create_inventory_dimension( + reference_document="Project", + type_of_transaction="Outward", + dimension_name="Project", + apply_to_all_doctypes=0, + document_type="Stock Ledger Entry", + ) + + self.assertFalse( + frappe.db.get_value( + "Custom Field", {"fieldname": "project", "dt": "Stock Ledger Entry"}, "name" + ) + ) + def prepare_test_data(): if not frappe.db.exists("DocType", "Shelf"): From 92f37ca111504eb3aa7092ec61db4513f24b2c61 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 31 Oct 2022 19:01:54 +0530 Subject: [PATCH 0433/1047] fix: Reset advance paid amount on Oreder cancel and amend --- erpnext/buying/doctype/purchase_order/purchase_order.js | 5 +++++ erpnext/selling/doctype/sales_order/sales_order.js | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index ddf81ca3ae..06fdea030c 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -101,6 +101,11 @@ frappe.ui.form.on("Purchase Order", { erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); }); + + // On cancel and amending a purchase order with advance payment, reset advance paid amount + if (frm.is_new()) { + frm.set_value("advance_paid", 0) + } }, apply_tds: function(frm) { diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 386c12b638..fb64772479 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -124,6 +124,11 @@ frappe.ui.form.on("Sales Order", { return query; }); + // On cancel and amending a sales order with advance payment, reset advance paid amount + if (frm.is_new()) { + frm.set_value("advance_paid", 0) + } + frm.ignore_doctypes_on_cancel_all = ['Purchase Order']; }, From 06e8e28531e2584fd5499df1c233082657de12b0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 31 Oct 2022 19:58:46 +0530 Subject: [PATCH 0434/1047] fix: Mode of payment for returns in POS Sales Invoice --- erpnext/controllers/taxes_and_totals.py | 41 +++++++++++++++---------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index cbcccce5f7..b5836c9070 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -889,24 +889,33 @@ class calculate_taxes_and_totals(object): self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc) def set_total_amount_to_default_mop(self, total_amount_to_pay): - default_mode_of_payment = frappe.db.get_value( - "POS Payment Method", - {"parent": self.doc.pos_profile, "default": 1}, - ["mode_of_payment"], - as_dict=1, - ) - - if default_mode_of_payment: - self.doc.payments = [] - self.doc.append( - "payments", - { - "mode_of_payment": default_mode_of_payment.mode_of_payment, - "amount": total_amount_to_pay, - "default": 1, - }, + total_paid_amount = 0 + for payment in self.doc.get("payments"): + total_paid_amount += ( + payment.amount if self.doc.party_account_currency == self.doc.currency else payment.base_amount ) + pending_amount = total_amount_to_pay - total_paid_amount + + if pending_amount > 0: + default_mode_of_payment = frappe.db.get_value( + "POS Payment Method", + {"parent": self.doc.pos_profile, "default": 1}, + ["mode_of_payment"], + as_dict=1, + ) + + if default_mode_of_payment: + self.doc.payments = [] + self.doc.append( + "payments", + { + "mode_of_payment": default_mode_of_payment.mode_of_payment, + "amount": pending_amount, + "default": 1, + }, + ) + def get_itemised_tax_breakup_html(doc): if not doc.taxes: From 4487065b6716ad3d48982df2d4642aa3b8447a80 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 1 Nov 2022 10:11:38 +0530 Subject: [PATCH 0435/1047] fix: update advance paid in SO/PO from Payment Ledger --- erpnext/controllers/accounts_controller.py | 40 ++++++++-------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 22291a3544..7f5dc0262d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -7,7 +7,7 @@ import json import frappe from frappe import _, throw from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied -from frappe.query_builder.functions import Sum +from frappe.query_builder.functions import Abs, Sum from frappe.utils import ( add_days, add_months, @@ -1334,30 +1334,20 @@ class AccountsController(TransactionBase): return stock_items def set_total_advance_paid(self): - if self.doctype == "Sales Order": - dr_or_cr = "credit_in_account_currency" - rev_dr_or_cr = "debit_in_account_currency" - party = self.customer - else: - dr_or_cr = "debit_in_account_currency" - rev_dr_or_cr = "credit_in_account_currency" - party = self.supplier - - advance = frappe.db.sql( - """ - select - account_currency, sum({dr_or_cr}) - sum({rev_dr_cr}) as amount - from - `tabGL Entry` - where - against_voucher_type = %s and against_voucher = %s and party=%s - and docstatus = 1 - """.format( - dr_or_cr=dr_or_cr, rev_dr_cr=rev_dr_or_cr - ), - (self.doctype, self.name, party), - as_dict=1, - ) # nosec + ple = frappe.qb.DocType("Payment Ledger Entry") + party = self.customer if self.doctype == "Sales Order" else self.supplier + advance = ( + frappe.qb.from_(ple) + .select(ple.account_currency, Abs(Sum(ple.amount)).as_("amount")) + .where( + (ple.against_voucher_type == self.doctype) + & (ple.against_voucher_no == self.name) + & (ple.party == party) + & (ple.delinked == 0) + & (ple.company == self.company) + ) + .run(as_dict=True) + ) if advance: advance = advance[0] From 9fb3fb4c836aef1d05e9a8160baf7089cd3bb224 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Tue, 1 Nov 2022 10:38:33 +0530 Subject: [PATCH 0436/1047] fix: use `flt` instead of `cint` in `get_batch_no` --- erpnext/stock/doctype/batch/batch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index 52854a0f01..f14288beb2 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -291,7 +291,7 @@ def get_batch_no(item_code, warehouse, qty=1, throw=False, serial_no=None): batches = get_batches(item_code, warehouse, qty, throw, serial_no) for batch in batches: - if cint(qty) <= cint(batch.qty): + if flt(qty) <= flt(batch.qty): batch_no = batch.batch_id break From 5a274176d3faf6a114a80d34a842abd63ef0428b Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Tue, 1 Nov 2022 11:45:17 +0530 Subject: [PATCH 0437/1047] feat: add asset_depreciation_schedule --- .../asset_depreciation_schedule/__init__.py | 0 .../asset_depreciation_schedule.js | 8 + .../asset_depreciation_schedule.json | 165 ++++++++++++++++++ .../asset_depreciation_schedule.py | 8 + .../test_asset_depreciation_schedule.py | 9 + 5 files changed, 190 insertions(+) create mode 100644 erpnext/assets/doctype/asset_depreciation_schedule/__init__.py create mode 100644 erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js create mode 100644 erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json create mode 100644 erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py create mode 100644 erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/__init__.py b/erpnext/assets/doctype/asset_depreciation_schedule/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js new file mode 100644 index 0000000000..9d41de381e --- /dev/null +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js @@ -0,0 +1,8 @@ +// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Asset Depreciation Schedule', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json new file mode 100644 index 0000000000..06eced229f --- /dev/null +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json @@ -0,0 +1,165 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "naming_series:", + "creation": "2022-10-31 15:03:35.424877", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset", + "column_break_2", + "naming_series", + "depreciation_details_section", + "finance_book", + "depreciation_method", + "rate_of_depreciation", + "column_break_8", + "total_number_of_depreciations", + "frequency_of_depreciation_months", + "depreciation_schedule_section", + "depreciation_schedule", + "details_section", + "notes", + "column_break_15", + "status", + "amended_from" + ], + "fields": [ + { + "fieldname": "asset", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Asset", + "options": "Asset", + "reqd": 1 + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "ACC-ADS-.YYYY.-" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Asset Depreciation Schedule", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "depreciation_details_section", + "fieldtype": "Section Break", + "label": "Depreciation Details" + }, + { + "fieldname": "finance_book", + "fieldtype": "Link", + "label": "Finance Book", + "options": "Finance Book", + "read_only": 1 + }, + { + "fieldname": "depreciation_method", + "fieldtype": "Select", + "label": "Depreciation Method", + "options": "\nStraight Line\nDouble Declining Balance\nWritten Down Value\nManual", + "read_only": 1 + }, + { + "depends_on": "eval:doc.depreciation_method == 'Written Down Value'", + "description": "In Percentage", + "fieldname": "rate_of_depreciation", + "fieldtype": "Percent", + "label": "Rate of Depreciation", + "read_only": 1 + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "depends_on": "total_number_of_depreciations", + "fieldname": "total_number_of_depreciations", + "fieldtype": "Int", + "label": "Total Number of Depreciations", + "read_only": 1 + }, + { + "depends_on": "frequency_of_depreciation_months", + "fieldname": "frequency_of_depreciation_months", + "fieldtype": "Int", + "label": "Frequency of Depreciation (Months)", + "read_only": 1 + }, + { + "fieldname": "depreciation_schedule_section", + "fieldtype": "Section Break", + "label": "Depreciation Schedule" + }, + { + "fieldname": "depreciation_schedule", + "fieldtype": "Table", + "label": "Depreciation Schedule", + "options": "Depreciation Schedule" + }, + { + "collapsible": 1, + "collapsible_depends_on": "notes", + "fieldname": "details_section", + "fieldtype": "Section Break", + "label": "Details" + }, + { + "fieldname": "notes", + "fieldtype": "Small Text", + "label": "Notes" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "hidden": 1, + "label": "Status", + "options": "Draft\nActive\nCancelled", + "read_only": 1 + }, + { + "fieldname": "column_break_15", + "fieldtype": "Column Break" + } + ], + "in_create": 1, + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2022-11-01 11:44:15.256470", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Depreciation Schedule", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py new file mode 100644 index 0000000000..625c3bdb1f --- /dev/null +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -0,0 +1,8 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class AssetDepreciationSchedule(Document): + pass diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py new file mode 100644 index 0000000000..21de8cde51 --- /dev/null +++ b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py @@ -0,0 +1,9 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestAssetDepreciationSchedule(FrappeTestCase): + pass From 65e855bfff6b0e2ca935c3f81a907ad9b6eb7e2d Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Tue, 1 Nov 2022 12:45:28 +0530 Subject: [PATCH 0438/1047] fix: pro_rata_amount calculation in assets tests --- erpnext/assets/doctype/asset/test_asset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 370b13bb98..5c1311d68a 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -221,7 +221,7 @@ class TestAsset(AssetSetup): asset.precision("gross_purchase_amount"), ) pro_rata_amount, _, _ = asset.get_pro_rata_amt( - asset.finance_books[0], 9000, add_months(get_last_day(purchase_date), 1), date + asset.finance_books[0], 9000, get_last_day(add_months(purchase_date, 1)), date ) pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount")) self.assertEquals(accumulated_depr_amount, 18000.00 + pro_rata_amount) @@ -283,7 +283,7 @@ class TestAsset(AssetSetup): self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold") pro_rata_amount, _, _ = asset.get_pro_rata_amt( - asset.finance_books[0], 9000, add_months(get_last_day(purchase_date), 1), date + asset.finance_books[0], 9000, get_last_day(add_months(purchase_date, 1)), date ) pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount")) From 935f31eff924e4266fe16fc0f4eb776e4ae53214 Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 1 Nov 2022 12:55:46 +0530 Subject: [PATCH 0439/1047] fix: test cases added for item group --- .../doctype/pricing_rule/test_pricing_rule.py | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index fbe567824f..1f7672c9d8 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -710,6 +710,132 @@ class TestPricingRule(unittest.TestCase): item.delete() + def test_item_group_price_with_blank_uom_pricing_rule(self): + group = frappe.get_doc(doctype="Item Group", item_group_name="_Test Pricing Rule Item Group") + group.save() + properties = { + "item_code": "Item with Group Blank UOM", + "item_group": "_Test Pricing Rule Item Group", + "stock_uom": "Nos", + "sales_uom": "Box", + "uoms": [dict(uom="Box", conversion_factor=10)], + } + item = make_item(properties=properties) + + make_item_price("Item with Group Blank UOM", "_Test Price List", 100) + + pricing_rule_record = { + "doctype": "Pricing Rule", + "title": "_Test Item with Group Blank UOM Rule", + "apply_on": "Item Group", + "item_groups": [ + { + "item_group": "_Test Pricing Rule Item Group", + } + ], + "selling": 1, + "currency": "INR", + "rate_or_discount": "Rate", + "rate": 101, + "company": "_Test Company", + } + rule = frappe.get_doc(pricing_rule_record) + rule.insert() + + si = create_sales_invoice( + do_not_save=True, item_code="Item with Group Blank UOM", uom="Box", conversion_factor=10 + ) + si.selling_price_list = "_Test Price List" + si.save() + + # If UOM is blank consider it as stock UOM and apply pricing_rule on all UOM. + # rate is 101, Selling UOM is Box that have conversion_factor of 10 so 101 * 10 = 1010 + self.assertEqual(si.items[0].price_list_rate, 1010) + self.assertEqual(si.items[0].rate, 1010) + + si.delete() + + si = create_sales_invoice(do_not_save=True, item_code="Item with Group Blank UOM", uom="Nos") + si.selling_price_list = "_Test Price List" + si.save() + + # UOM is blank so consider it as stock UOM and apply pricing_rule on all UOM. + # rate is 101, Selling UOM is Nos that have conversion_factor of 1 so 101 * 1 = 101 + self.assertEqual(si.items[0].price_list_rate, 101) + self.assertEqual(si.items[0].rate, 101) + + si.delete() + rule.delete() + frappe.get_doc("Item Price", {"item_code": "Item with Group Blank UOM"}).delete() + item.delete() + group.delete() + + def test_item_group_price_with_selling_uom_pricing_rule(self): + group = frappe.get_doc(doctype="Item Group", item_group_name="_Test Pricing Rule Item Group UOM") + group.save() + properties = { + "item_code": "Item with Group UOM other than Stock", + "item_group": "_Test Pricing Rule Item Group UOM", + "stock_uom": "Nos", + "sales_uom": "Box", + "uoms": [dict(uom="Box", conversion_factor=10)], + } + item = make_item(properties=properties) + + make_item_price("Item with Group UOM other than Stock", "_Test Price List", 100) + + pricing_rule_record = { + "doctype": "Pricing Rule", + "title": "_Test Item with Group UOM other than Stock Rule", + "apply_on": "Item Group", + "item_groups": [ + { + "item_group": "_Test Pricing Rule Item Group UOM", + "uom": "Box", + } + ], + "selling": 1, + "currency": "INR", + "rate_or_discount": "Rate", + "rate": 101, + "company": "_Test Company", + } + rule = frappe.get_doc(pricing_rule_record) + rule.insert() + + si = create_sales_invoice( + do_not_save=True, + item_code="Item with Group UOM other than Stock", + uom="Box", + conversion_factor=10, + ) + si.selling_price_list = "_Test Price List" + si.save() + + # UOM is Box so apply pricing_rule only on Box UOM. + # Selling UOM is Box and as both UOM are same no need to multiply by conversion_factor. + self.assertEqual(si.items[0].price_list_rate, 101) + self.assertEqual(si.items[0].rate, 101) + + si.delete() + + si = create_sales_invoice( + do_not_save=True, item_code="Item with Group UOM other than Stock", uom="Nos" + ) + si.selling_price_list = "_Test Price List" + si.save() + + # UOM is Box so pricing_rule won't apply as selling_uom is Nos. + # As Pricing Rule is not applied price of 100 will be fetched from Item Price List. + self.assertEqual(si.items[0].price_list_rate, 100) + self.assertEqual(si.items[0].rate, 100) + + si.delete() + rule.delete() + frappe.get_doc("Item Price", {"item_code": "Item with Group UOM other than Stock"}).delete() + item.delete() + group.delete() + def test_pricing_rule_for_different_currency(self): make_item("Test Sanitizer Item") From 672fbd38498dc8031588c7a0ba1d6c4327db6935 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Tue, 1 Nov 2022 14:25:38 +0530 Subject: [PATCH 0440/1047] chore: empty commit to try fixing stuck test From 1d83fb20d6678a2495c128380969b673ebc41b1a Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Tue, 1 Nov 2022 16:39:32 +0530 Subject: [PATCH 0441/1047] feat(pricing rule): free qty rounding and recursion qty (#32577) Option to specify recursion start qty and repeating qty Co-authored-by: Deepesh Garg --- .../doctype/pricing_rule/pricing_rule.json | 27 ++++++++++++- .../doctype/pricing_rule/pricing_rule.py | 13 +++++++ .../doctype/pricing_rule/test_pricing_rule.py | 39 +++++++++++++++++++ .../accounts/doctype/pricing_rule/utils.py | 8 +++- erpnext/public/js/controllers/transaction.js | 3 +- 5 files changed, 86 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index 6e7ebd1414..ce9ce647db 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -52,7 +52,10 @@ "free_item_rate", "column_break_42", "free_item_uom", + "round_free_qty", "is_recursive", + "recurse_for", + "apply_recursion_over", "section_break_23", "valid_from", "valid_upto", @@ -578,12 +581,34 @@ "fieldtype": "Select", "label": "Naming Series", "options": "PRLE-.####" + }, + { + "default": "0", + "fieldname": "round_free_qty", + "fieldtype": "Check", + "label": "Round Free Qty" + }, + { + "depends_on": "is_recursive", + "description": "Give free item for every N quantity", + "fieldname": "recurse_for", + "fieldtype": "Float", + "label": "Recurse Every (As Per Transaction UOM)", + "mandatory_depends_on": "is_recursive" + }, + { + "default": "0", + "depends_on": "is_recursive", + "description": "Qty for which recursion isn't applicable.", + "fieldname": "apply_recursion_over", + "fieldtype": "Float", + "label": "Apply Recursion Over (As Per Transaction UOM)" } ], "icon": "fa fa-gift", "idx": 1, "links": [], - "modified": "2022-09-16 16:00:38.356266", + "modified": "2022-10-13 19:05:35.056304", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 826d71b12e..ed46d85e3a 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -24,6 +24,7 @@ class PricingRule(Document): self.validate_applicable_for_selling_or_buying() self.validate_min_max_amt() self.validate_min_max_qty() + self.validate_recursion() self.cleanup_fields_value() self.validate_rate_or_discount() self.validate_max_discount() @@ -109,6 +110,18 @@ class PricingRule(Document): if self.min_amt and self.max_amt and flt(self.min_amt) > flt(self.max_amt): throw(_("Min Amt can not be greater than Max Amt")) + def validate_recursion(self): + if self.price_or_product_discount != "Product": + return + if self.free_item or self.same_item: + if flt(self.recurse_for) <= 0: + self.recurse_for = 1 + if self.is_recursive: + if flt(self.apply_recursion_over) > flt(self.min_qty): + throw(_("Min Qty should be greater than Recurse Over Qty")) + if flt(self.apply_recursion_over) < 0: + throw(_("Recurse Over Qty cannot be less than 0")) + def cleanup_fields_value(self): for logic_field in ["apply_on", "applicable_for", "rate_or_discount"]: fieldname = frappe.scrub(self.get(logic_field) or "") diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 1f7672c9d8..d27f65eba0 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -1069,6 +1069,45 @@ class TestPricingRule(unittest.TestCase): si.delete() rule.delete() + def test_pricing_rule_for_product_free_item_rounded_qty_and_recursion(self): + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule") + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule", + "apply_on": "Item Code", + "currency": "USD", + "items": [ + { + "item_code": "_Test Item", + } + ], + "selling": 1, + "rate": 0, + "min_qty": 3, + "max_qty": 7, + "price_or_product_discount": "Product", + "same_item": 1, + "free_qty": 1, + "round_free_qty": 1, + "is_recursive": 1, + "recurse_for": 2, + "company": "_Test Company", + } + frappe.get_doc(test_record.copy()).insert() + + # With pricing rule + so = make_sales_order(item_code="_Test Item", qty=5) + so.load_from_db() + self.assertEqual(so.items[1].is_free_item, 1) + self.assertEqual(so.items[1].item_code, "_Test Item") + self.assertEqual(so.items[1].qty, 2) + + so = make_sales_order(item_code="_Test Item", qty=7) + so.load_from_db() + self.assertEqual(so.items[1].is_free_item, 1) + self.assertEqual(so.items[1].item_code, "_Test Item") + self.assertEqual(so.items[1].qty, 4) + test_dependencies = ["Campaign"] diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index f87fecf9bf..bb54b23e26 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -633,9 +633,13 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): qty = pricing_rule.free_qty or 1 if pricing_rule.is_recursive: - transaction_qty = args.get("qty") if args else doc.total_qty + transaction_qty = ( + args.get("qty") if args else doc.total_qty + ) - pricing_rule.apply_recursion_over if transaction_qty: - qty = flt(transaction_qty) * qty + qty = flt(transaction_qty) * qty / pricing_rule.recurse_for + if pricing_rule.round_free_qty: + qty = round(qty) free_item_data_args = { "item_code": free_item, diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 7fecb18fad..dd957c72ac 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1404,7 +1404,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe if (!r.exc && r.message) { me._set_values_for_item_list(r.message); if(item) me.set_gross_profit(item); - if(me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on") + if (me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on") } } }); @@ -1577,6 +1577,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe for (let key in pr_row) { row_to_modify[key] = pr_row[key]; } + this.frm.script_manager.copy_from_first_row("items", row_to_modify, ["expense_account", "income_account"]); }); // free_item_data is a temporary variable From ddd1b4be3ff1daea9e9abc922c62c4abea5be951 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 1 Nov 2022 19:30:37 +0530 Subject: [PATCH 0442/1047] fix: test cases --- erpnext/stock/doctype/material_request/test_material_request.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 78af1532ea..005480e399 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -591,6 +591,7 @@ class TestMaterialRequest(FrappeTestCase): mr.material_request_type = "Material Issue" mr.submit() + self.assertTrue(frappe.db.exists("Material Request", mr.name)) # testing bin value after material request is submitted self.assertEqual(_get_requested_qty(), existing_requested_qty - 54.0) From 8ea69837348e4333f3b950a82b755076f58b8751 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Tue, 1 Nov 2022 19:30:06 +0530 Subject: [PATCH 0443/1047] fix: `Material Consumption` option in case of `Skip Transfer to WIP` in WO --- .../doctype/work_order/work_order.js | 101 +++++++++--------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 4aab3fa373..6247618248 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -589,66 +589,69 @@ erpnext.work_order = { } } - if(!frm.doc.skip_transfer){ + if (frm.doc.status != 'Stopped') { // If "Material Consumption is check in Manufacturing Settings, allow Material Consumption - if (flt(doc.material_transferred_for_manufacturing) > 0 && frm.doc.status != 'Stopped') { - if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing))) { - frm.has_finish_btn = true; - - if (frm.doc.__onload && frm.doc.__onload.material_consumption == 1) { - // Only show "Material Consumption" when required_qty > consumed_qty - var counter = 0; - var tbl = frm.doc.required_items || []; - var tbl_lenght = tbl.length; - for (var i = 0, len = tbl_lenght; i < len; i++) { - let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty; - if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) { - counter += 1; - } - } - if (counter > 0) { - var consumption_btn = frm.add_custom_button(__('Material Consumption'), function() { - const backflush_raw_materials_based_on = frm.doc.__onload.backflush_raw_materials_based_on; - erpnext.work_order.make_consumption_se(frm, backflush_raw_materials_based_on); - }); - consumption_btn.addClass('btn-primary'); + if (frm.doc.__onload && frm.doc.__onload.material_consumption == 1) { + if (flt(doc.material_transferred_for_manufacturing) > 0 || frm.doc.skip_transfer) { + // Only show "Material Consumption" when required_qty > consumed_qty + var counter = 0; + var tbl = frm.doc.required_items || []; + var tbl_lenght = tbl.length; + for (var i = 0, len = tbl_lenght; i < len; i++) { + let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty; + if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) { + counter += 1; } } + if (counter > 0) { + var consumption_btn = frm.add_custom_button(__('Material Consumption'), function() { + const backflush_raw_materials_based_on = frm.doc.__onload.backflush_raw_materials_based_on; + erpnext.work_order.make_consumption_se(frm, backflush_raw_materials_based_on); + }); + consumption_btn.addClass('btn-primary'); + } + } + } + if(!frm.doc.skip_transfer){ + if (flt(doc.material_transferred_for_manufacturing) > 0) { + if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing))) { + frm.has_finish_btn = true; + + var finish_btn = frm.add_custom_button(__('Finish'), function() { + erpnext.work_order.make_se(frm, 'Manufacture'); + }); + + if(doc.material_transferred_for_manufacturing>=doc.qty) { + // all materials transferred for manufacturing, make this primary + finish_btn.addClass('btn-primary'); + } + } else { + frappe.db.get_doc("Manufacturing Settings").then((doc) => { + let allowance_percentage = doc.overproduction_percentage_for_work_order; + + if (allowance_percentage > 0) { + let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty); + + if ((flt(doc.produced_qty) < allowed_qty)) { + frm.add_custom_button(__('Finish'), function() { + erpnext.work_order.make_se(frm, 'Manufacture'); + }); + } + } + }); + } + } + } else { + if ((flt(doc.produced_qty) < flt(doc.qty))) { var finish_btn = frm.add_custom_button(__('Finish'), function() { erpnext.work_order.make_se(frm, 'Manufacture'); }); - - if(doc.material_transferred_for_manufacturing>=doc.qty) { - // all materials transferred for manufacturing, make this primary - finish_btn.addClass('btn-primary'); - } - } else { - frappe.db.get_doc("Manufacturing Settings").then((doc) => { - let allowance_percentage = doc.overproduction_percentage_for_work_order; - - if (allowance_percentage > 0) { - let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty); - - if ((flt(doc.produced_qty) < allowed_qty)) { - frm.add_custom_button(__('Finish'), function() { - erpnext.work_order.make_se(frm, 'Manufacture'); - }); - } - } - }); + finish_btn.addClass('btn-primary'); } } - } else { - if ((flt(doc.produced_qty) < flt(doc.qty)) && frm.doc.status != 'Stopped') { - var finish_btn = frm.add_custom_button(__('Finish'), function() { - erpnext.work_order.make_se(frm, 'Manufacture'); - }); - finish_btn.addClass('btn-primary'); - } } } - }, calculate_cost: function(doc) { if (doc.operations){ From f7c9258770a79aff0b951dc18c0b7c39f794b03f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 1 Nov 2022 19:54:41 +0530 Subject: [PATCH 0444/1047] fix: Issues while cancel/amending Purchase Invoice with TDS enabled --- .../accounts/doctype/purchase_invoice/purchase_invoice.js | 4 ++++ .../accounts/doctype/purchase_invoice/purchase_invoice.py | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index c3a9855ff4..39a623519a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -569,6 +569,10 @@ frappe.ui.form.on("Purchase Invoice", { erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); }); + + if (frm.is_new()) { + frm.clear_table("tax_withheld_vouchers"); + } }, is_subcontracted: function(frm) { diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 3d74b8f139..882a374046 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -71,6 +71,9 @@ class PurchaseInvoice(BuyingController): supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category") self.set_onload("supplier_tds", supplier_tds) + if self.is_new(): + self.set("tax_withheld_vouchers", []) + def before_save(self): if not self.on_hold: self.release_date = "" @@ -1415,7 +1418,7 @@ class PurchaseInvoice(BuyingController): "Stock Ledger Entry", "Repost Item Valuation", "Payment Ledger Entry", - "Purchase Invoice", + "Tax Withheld Vouchers", ) self.update_advance_tax_references(cancel=1) From 5b74161195b3b006d47f29de5a97428effca527e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 1 Nov 2022 20:17:34 +0530 Subject: [PATCH 0445/1047] chore: Update tests --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 1ba782451b..cb0d1a75a0 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -965,7 +965,8 @@ class TestSalesInvoice(unittest.TestCase): pos_return.insert() pos_return.submit() - self.assertEqual(pos_return.get("payments")[0].amount, -1000) + self.assertEqual(pos_return.get("payments")[0].amount, -500) + self.assertEqual(pos_return.get("payments")[1].amount, -500) def test_pos_change_amount(self): make_pos_profile( From 3f2728e3f7645080b20a6835da6bb39bbd084b11 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 1 Nov 2022 19:56:55 +0530 Subject: [PATCH 0446/1047] test: run tmate --- erpnext/stock/doctype/material_request/test_material_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 005480e399..f02462c596 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -590,8 +590,8 @@ class TestMaterialRequest(FrappeTestCase): mr = frappe.copy_doc(test_records[0]) mr.material_request_type = "Material Issue" mr.submit() + frappe.db.value_cache = {} - self.assertTrue(frappe.db.exists("Material Request", mr.name)) # testing bin value after material request is submitted self.assertEqual(_get_requested_qty(), existing_requested_qty - 54.0) From 579afed4606aa55b8e2d17cfab37b2a3cda94854 Mon Sep 17 00:00:00 2001 From: Muvuk Date: Tue, 1 Nov 2022 19:23:00 +0100 Subject: [PATCH 0447/1047] feat: Optional Hungarian COA for microenterprises #32688 --- ..._microenterprises_with_account_number.json | 1654 +++++++++++++++++ 1 file changed, 1654 insertions(+) create mode 100644 erpnext/accounts/doctype/account/chart_of_accounts/verified/hu_chart_of_accounts_for_microenterprises_with_account_number.json diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/hu_chart_of_accounts_for_microenterprises_with_account_number.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/hu_chart_of_accounts_for_microenterprises_with_account_number.json new file mode 100644 index 0000000000..2cd6c0fc61 --- /dev/null +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/hu_chart_of_accounts_for_microenterprises_with_account_number.json @@ -0,0 +1,1654 @@ +{ + "tree": { + "SZ\u00c1MLAOSZT\u00c1LY BEFEKTETETT ESZK\u00d6Z\u00d6K": { + "account_number": 1, + "root_type": "Asset", + "is_group": 1, + "IMMATERI\u00c1LIS JAVAK": { + "account_number": 11, + "root_type": "Asset", + "is_group": 1, + "Vagyoni \u00e9rt\u00e9k\u0171 jogok": { + "account_number": 113, + "root_type": "Asset", + "is_group": 1, + "Vagyoni \u00e9rt\u00e9k\u0171 jogok brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1131, + "root_type": "Asset" + }, + "Vagyoni \u00e9rt\u00e9k\u0171 jogok terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1138, + "root_type": "Asset" + }, + "Vagyoni \u00e9rt\u00e9k\u0171 jogok terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1139, + "root_type": "Asset" + } + }, + "Szellemi term\u00e9kek": { + "account_number": 114, + "root_type": "Asset", + "is_group": 1, + "Szellemi term\u00e9kek brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1141, + "root_type": "Asset" + }, + "Szellemi term\u00e9kek terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1148, + "root_type": "Asset" + }, + "Szellemi term\u00e9kek terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1149, + "root_type": "Asset" + } + }, + "Kis \u00e9rt\u00e9k\u0171 immateri\u00e1lis javak": { + "account_number": 119, + "root_type": "Asset", + "is_group": 1, + "Kis \u00e9rt\u00e9k\u0171 immateri\u00e1lis javak brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1191, + "root_type": "Asset" + }, + "Kis \u00e9rt\u00e9k\u0171 immateri\u00e1lis javak terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1199, + "root_type": "Asset" + } + } + }, + "INGATLANOK \u00c9S KAPCSOL\u00d3D\u00d3 VAGYONI \u00c9RT\u00c9K\u0170 JOGOK": { + "account_number": 12, + "root_type": "Asset", + "is_group": 1, + "Telkek, f\u00f6ldter\u00fcletek": { + "account_number": 121, + "root_type": "Asset", + "is_group": 1, + "Telkek, f\u00f6ldter\u00fcletek brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1211, + "root_type": "Asset" + }, + "Telkek, f\u00f6ldter\u00fcletek terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1218, + "root_type": "Asset" + }, + "Telkek, f\u00f6ldter\u00fcletek terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1219, + "root_type": "Asset" + } + }, + "Ingatlanokhoz kapcsol\u00f3d\u00f3 vagyoni \u00e9rt\u00e9k\u0171 jogok": { + "account_number": 122, + "root_type": "Asset", + "is_group": 1, + "Ingatlanokhoz kapcsol\u00f3d\u00f3 vagyoni \u00e9rt\u00e9k\u0171 jogok brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1221, + "root_type": "Asset" + }, + "Ingatlanokhoz kapcsol\u00f3d\u00f3 vagyoni \u00e9rt\u00e9k\u0171 jogok terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1228, + "root_type": "Asset" + }, + "Ingatlanokhoz kapcsol\u00f3d\u00f3 vagyoni \u00e9rt\u00e9k\u0171 jogok terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1229, + "root_type": "Asset" + } + }, + "\u00c9p\u00fcletek, \u00e9p\u00fcletr\u00e9szek, tulajdoni h\u00e1nyadok": { + "account_number": 123, + "root_type": "Asset", + "is_group": 1, + "\u00c9p\u00fcletek, \u00e9p\u00fcletr\u00e9szek, tulajdoni h\u00e1nyadok brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1231, + "root_type": "Asset" + }, + "\u00c9p\u00fcletek, \u00e9p\u00fcletr\u00e9szek, tulajdoni h\u00e1nyadok terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1238, + "root_type": "Asset" + }, + "\u00c9p\u00fcletek, \u00e9p\u00fcletr\u00e9szek, tulajdoni h\u00e1nyadok terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1239, + "root_type": "Asset" + } + }, + "Egy\u00e9b ingatlanok": { + "account_number": 124, + "root_type": "Asset", + "is_group": 1, + "Egy\u00e9b ingatlanok brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1241, + "root_type": "Asset" + }, + "Egy\u00e9b ingatlanok terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1248, + "root_type": "Asset" + }, + "Egy\u00e9b ingatlanok terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1249, + "root_type": "Asset" + } + }, + "Kis \u00e9rt\u00e9k\u0171 ingatlanok": { + "account_number": 129, + "root_type": "Asset", + "is_group": 1, + "Kis \u00e9rt\u00e9k\u0171 ingatlanok brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1291, + "root_type": "Asset" + }, + "Kis \u00e9rt\u00e9k\u0171 ingatlanok terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1299, + "root_type": "Asset" + } + } + }, + "M\u0170SZAKI BERENDEZ\u00c9SEK, G\u00c9PEK, J\u00c1RM\u0170VEK": { + "account_number": 13, + "root_type": "Asset", + "is_group": 1, + "Termel\u0151 g\u00e9pek, berendez\u00e9sek": { + "account_number": 131, + "root_type": "Asset", + "is_group": 1, + "Termel\u0151 g\u00e9pek, berendez\u00e9sek brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1311, + "root_type": "Asset" + }, + "Termel\u0151 g\u00e9pek, berendez\u00e9sek terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1318, + "root_type": "Asset" + }, + "Termel\u0151 g\u00e9pek, berendez\u00e9sek terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1319, + "root_type": "Asset" + } + }, + "M\u0171szaki j\u00e1rm\u0171vek": { + "account_number": 132, + "root_type": "Asset", + "is_group": 1, + "M\u0171szaki j\u00e1rm\u0171vek brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1321, + "root_type": "Asset" + }, + "M\u0171szaki j\u00e1rm\u0171vek terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1328, + "root_type": "Asset" + }, + "M\u0171szaki j\u00e1rm\u0171vek terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1329, + "root_type": "Asset" + } + }, + "Ki nem emelt m\u0171szaki berendez\u00e9sek, g\u00e9pek, j\u00e1rm\u0171vek": { + "account_number": 133, + "root_type": "Asset", + "is_group": 1, + "Ki nem emelt m\u0171szaki berendez\u00e9sek, g\u00e9pek, j\u00e1rm\u0171vek brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1331, + "root_type": "Asset" + }, + "Ki nem emelt m\u0171szaki berendez\u00e9sek, g\u00e9pek, j\u00e1rm\u0171vek terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1338, + "root_type": "Asset" + }, + "Ki nem emelt m\u0171szaki berendez\u00e9sek, g\u00e9pek, j\u00e1rm\u0171vek terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1339, + "root_type": "Asset" + } + }, + "Kis \u00e9rt\u00e9k\u0171 m\u0171szaki berendez\u00e9sek, g\u00e9pek, j\u00e1rm\u0171vek": { + "account_number": 139, + "root_type": "Asset", + "is_group": 1, + "Kis \u00e9rt\u00e9k\u0171 m\u0171szaki berendez\u00e9sek, g\u00e9pek, j\u00e1rm\u0171vek brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1391, + "root_type": "Asset" + }, + "Kis \u00e9rt\u00e9k\u0171 m\u0171szaki berendez\u00e9sek, g\u00e9pek, j\u00e1rm\u0171vek terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1399, + "root_type": "Asset" + } + } + }, + "EGY\u00c9B BERENDEZ\u00c9SEK, FELSZEREL\u00c9SEK, J\u00c1RM\u0170VEK": { + "account_number": 14, + "root_type": "Asset", + "is_group": 1, + "Egy\u00e9b (\u00fczemi \u00fczleti), berendez\u00e9sek, felszerel\u00e9sek": { + "account_number": 141, + "root_type": "Asset", + "is_group": 1, + "Egy\u00e9b (\u00fczemi \u00fczleti), berendez\u00e9sek, felszerel\u00e9sek brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1411, + "root_type": "Asset" + }, + "Egy\u00e9b (\u00fczemi \u00fczleti), berendez\u00e9sek, felszerel\u00e9sek terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1418, + "root_type": "Asset" + }, + "Egy\u00e9b (\u00fczemi \u00fczleti), berendez\u00e9sek, felszerel\u00e9sek terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1419, + "root_type": "Asset" + } + }, + "Egy\u00e9b j\u00e1rm\u0171vek": { + "account_number": 142, + "root_type": "Asset", + "is_group": 1, + "Egy\u00e9b j\u00e1rm\u0171vek brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1421, + "root_type": "Asset" + }, + "Egy\u00e9b j\u00e1rm\u0171vek terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1428, + "root_type": "Asset" + }, + "Egy\u00e9b j\u00e1rm\u0171vek terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1429, + "root_type": "Asset" + } + }, + "Irodai, igazgat\u00e1si berendez\u00e9sek \u00e9s felszerel\u00e9sek": { + "account_number": 143, + "root_type": "Asset", + "is_group": 1, + "Irodai, igazgat\u00e1si berendez\u00e9sek \u00e9s felszerel\u00e9sek brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1431, + "root_type": "Asset" + }, + "Irodai, igazgat\u00e1si berendez\u00e9sek \u00e9s felszerel\u00e9sek terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1438, + "root_type": "Asset" + }, + "Irodai, igazgat\u00e1si berendez\u00e9sek \u00e9s felszerel\u00e9sek terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1439, + "root_type": "Asset" + } + }, + "Ki nem emelt egy\u00e9b berendez\u00e9sek, felszerel\u00e9sek": { + "account_number": 144, + "root_type": "Asset", + "is_group": 1, + "Ki nem emelt egy\u00e9b berendez\u00e9sek, felszerel\u00e9sek brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1441, + "root_type": "Asset" + }, + "Ki nem emelt egy\u00e9b berendez\u00e9sek, felszerel\u00e9sek terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1448, + "root_type": "Asset" + }, + "Ki nem emelt egy\u00e9b berendez\u00e9sek, felszerel\u00e9sek terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1449, + "root_type": "Asset" + } + }, + "Kis \u00e9rt\u00e9k\u0171 egy\u00e9b berendez\u00e9sek, felszerel\u00e9sek, j\u00e1rm\u0171vek": { + "account_number": 149, + "root_type": "Asset", + "is_group": 1, + "Kis \u00e9rt\u00e9k\u0171 egy\u00e9b berendez\u00e9sek, felszerel\u00e9sek, j\u00e1rm\u0171vek brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1491, + "root_type": "Asset" + }, + "Kis \u00e9rt\u00e9k\u0171 egy\u00e9b berendez\u00e9sek, felszerel\u00e9sek, j\u00e1rm\u0171vek terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1499, + "root_type": "Asset" + } + } + }, + "TENY\u00c9SZ\u00c1LLATOK": { + "account_number": 15, + "root_type": "Asset", + "is_group": 1, + "Teny\u00e9szt\u00e9sben hasznos\u00edtott \u00e1llatok": { + "account_number": 151, + "root_type": "Asset", + "is_group": 1, + "Teny\u00e9szt\u00e9sben hasznos\u00edtott \u00e1llatok brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1511, + "root_type": "Asset" + }, + "Teny\u00e9szt\u00e9sben hasznos\u00edtott \u00e1llatok terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1518, + "root_type": "Asset" + }, + "Teny\u00e9szt\u00e9sben hasznos\u00edtott \u00e1llatok terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1519, + "root_type": "Asset" + } + }, + "Ig\u00e1s\u00e1llatok": { + "account_number": 152, + "root_type": "Asset", + "is_group": 1, + "Ig\u00e1s\u00e1llatok brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1521, + "root_type": "Asset" + }, + "Ig\u00e1s\u00e1llatok terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1528, + "root_type": "Asset" + }, + "Ig\u00e1s\u00e1llatok terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1529, + "root_type": "Asset" + } + }, + "Egy\u00e9b teny\u00e9sz\u00e1llatok": { + "account_number": 153, + "root_type": "Asset", + "is_group": 1, + "Egy\u00e9b teny\u00e9sz\u00e1llatok brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1531, + "root_type": "Asset" + }, + "Egy\u00e9b teny\u00e9sz\u00e1llatok terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 1538, + "root_type": "Asset" + }, + "Egy\u00e9b teny\u00e9sz\u00e1llatok terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1539, + "root_type": "Asset" + } + }, + "Kis \u00e9rt\u00e9k\u0171 teny\u00e9sz\u00e1llatok": { + "account_number": 159, + "root_type": "Asset", + "is_group": 1, + "Kis \u00e9rt\u00e9k\u0171 teny\u00e9sz\u00e1llatok brutt\u00f3 \u00e9rt\u00e9ke": { + "account_number": 1591, + "root_type": "Asset" + }, + "Kis \u00e9rt\u00e9k\u0171 teny\u00e9sz\u00e1llatok terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 1599, + "root_type": "Asset" + } + } + }, + "BERUH\u00c1Z\u00c1SOK, FEL\u00daJ\u00cdT\u00c1SOK": { + "account_number": 16, + "root_type": "Asset", + "is_group": 1, + "Beruh\u00e1z\u00e1sok": { + "account_number": 161, + "root_type": "Asset" + }, + "Fel\u00faj\u00edt\u00e1sok": { + "account_number": 162, + "root_type": "Asset" + }, + "Beruh\u00e1z\u00e1sok, fel\u00faj\u00edt\u00e1sok terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 168, + "root_type": "Asset" + } + }, + "TULAJDONI R\u00c9SZESED\u00c9ST JELENT\u0150 BEFEKTET\u00c9SEK (R\u00c9SZESED\u00c9SEK)": { + "account_number": 17, + "root_type": "Asset", + "is_group": 1, + "Tart\u00f3s r\u00e9szesed\u00e9sek": { + "account_number": 171, + "root_type": "Asset" + }, + "R\u00e9szesed\u00e9sek \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 179, + "root_type": "Asset" + } + }, + "HITELVISZONYT MEGTESTES\u00cdT\u0150 \u00c9RT\u00c9KPAP\u00cdROK": { + "account_number": 18, + "root_type": "Asset", + "is_group": 1, + "\u00c1llamk\u00f6tv\u00e9nyek": { + "account_number": 181, + "root_type": "Asset" + }, + "Tart\u00f3s diszkont \u00e9rt\u00e9kpap\u00edrok": { + "account_number": 182, + "root_type": "Asset" + }, + "V\u00e1llalkoz\u00e1sok \u00e1ltal kibocs\u00e1tott tart\u00f3s \u00e9rt\u00e9kpap\u00edrok": { + "account_number": 183, + "root_type": "Asset" + }, + "Egy\u00e9b hitelviszonyt megtestes\u00edt\u0151 \u00e9rt\u00e9kpap\u00edrok": { + "account_number": 184, + "root_type": "Asset" + }, + "Tart\u00f3s \u00e9rt\u00e9kpap\u00edrok \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 189, + "root_type": "Asset" + } + }, + "TART\u00d3SAN ADOTT K\u00d6LCS\u00d6N\u00d6K (tart\u00f3s bankbet\u00e9tek)": { + "account_number": 19, + "root_type": "Asset", + "is_group": 1, + "Tart\u00f3san adott k\u00f6lcs\u00f6n\u00f6k": { + "account_number": 191, + "root_type": "Asset" + }, + "Tart\u00f3s bankbet\u00e9tek": { + "account_number": 192, + "root_type": "Asset" + }, + "Tart\u00f3san adott k\u00f6lcs\u00f6n\u00f6k (\u00e9s bankbet\u00e9tek) \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 199, + "root_type": "Asset" + } + } + }, + "SZ\u00c1MLAOSZT\u00c1LY K\u00c9SZLETEK": { + "account_number": 2, + "root_type": "Asset", + "is_group": 1, + "ANYAGOK": { + "account_number": "21-22", + "root_type": "Asset", + "is_group": 1, + "Nyers \u00e9s alapanyagok": { + "account_number": 211, + "root_type": "Asset" + }, + "Seg\u00e9danyagok": { + "account_number": 221, + "root_type": "Asset" + }, + "\u00dczem \u00e9s f\u0171t\u0151anyagok": { + "account_number": 222, + "root_type": "Asset" + }, + "Fenntart\u00e1si anyagok": { + "account_number": 223, + "root_type": "Asset" + }, + "\u00c9p\u00edt\u00e9si anyagok": { + "account_number": 224, + "root_type": "Asset" + }, + "Egy \u00e9ven bel\u00fcl elhaszn\u00e1l\u00f3d\u00f3 anyagi eszk\u00f6z\u00f6k": { + "account_number": 225, + "root_type": "Asset" + }, + "T\u00e1rgyi eszk\u00f6z\u00f6k k\u00f6z\u00fcl \u00e1tsorolt anyagok": { + "account_number": 226, + "root_type": "Asset" + }, + "Egy\u00e9b anyagok": { + "account_number": 227, + "root_type": "Asset" + }, + "Anyagok \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 229, + "root_type": "Asset" + } + }, + "BEFEJEZETLEN TERMEL\u00c9S \u00c9S F\u00c9LK\u00c9SZ TERM\u00c9KEK": { + "account_number": 23, + "root_type": "Asset", + "is_group": 1, + "Befejezetlen termel\u00e9s": { + "account_number": 231, + "root_type": "Asset" + }, + "F\u00e9lk\u00e9sz term\u00e9kek": { + "account_number": 232, + "root_type": "Asset" + }, + "Befejezetlen termel\u00e9s \u00e9s f\u00e9lk\u00e9sz term\u00e9kek \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 239, + "root_type": "Asset" + } + }, + "N\u00d6VEND\u00c9K, H\u00cdZ\u00d3 \u00c9S EGY\u00c9B \u00c1LLATOK": { + "account_number": 24, + "root_type": "Asset", + "is_group": 1, + "N\u00f6vend\u00e9k\u00e1llatok": { + "account_number": 241, + "root_type": "Asset" + }, + "H\u00edz\u00f3\u00e1llatok": { + "account_number": 242, + "root_type": "Asset" + }, + "Egy\u00e9b \u00e1llatok": { + "account_number": 243, + "root_type": "Asset" + }, + "B\u00e9rbevett \u00e1llatok": { + "account_number": 244, + "root_type": "Asset" + }, + "\u00c1llatok \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 249, + "root_type": "Asset" + } + }, + "K\u00c9SZTERM\u00c9KEK": { + "account_number": 25, + "root_type": "Asset", + "is_group": 1, + "K\u00e9szterm\u00e9kek": { + "account_number": 251, + "root_type": "Asset" + }, + "K\u00e9szterm\u00e9kek \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 259, + "root_type": "Asset" + } + }, + "KERESKEDELMI \u00c1RUK": { + "account_number": 26, + "root_type": "Asset", + "is_group": 1, + "Kereskedelmi \u00e1ruk": { + "account_number": 261, + "root_type": "Asset" + }, + "Idegen helyen t\u00e1rolt, bizom\u00e1nyba \u00e1tadott \u00e1ruk": { + "account_number": 262, + "root_type": "Asset" + }, + "T\u00e1rgyi eszk\u00f6z\u00f6k k\u00f6z\u00fcl \u00e1tsorolt \u00e1ruk": { + "account_number": 263, + "root_type": "Asset" + }, + "Bels\u0151 (egys\u00e9gek, tev\u00e9kenys\u00e9gek k\u00f6z\u00f6tti) \u00e1tad\u00e1s\u00e1tv\u00e9tel \u00fctk\u00f6z\u0151sz\u00e1mla": { + "account_number": 264, + "root_type": "Asset" + }, + "Kereskedelmi \u00e1ruk \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 269, + "root_type": "Asset" + } + }, + "K\u00d6ZVET\u00cdTETT SZOLG\u00c1LTAT\u00c1SOK": { + "account_number": 27, + "root_type": "Asset", + "is_group": 1, + "K\u00f6zvet\u00edtett szolg\u00e1ltat\u00e1sok": { + "account_number": 271, + "root_type": "Asset" + }, + "K\u00f6zvet\u00edtett szolg\u00e1ltat\u00e1sok \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 279, + "root_type": "Asset" + } + }, + "BET\u00c9TD\u00cdJAS G\u00d6NGY\u00d6LEGEK": { + "account_number": 28, + "root_type": "Asset", + "is_group": 1, + "Bet\u00e9td\u00edjas g\u00f6ngy\u00f6legek": { + "account_number": 281, + "root_type": "Asset" + }, + "Bet\u00e9td\u00edjas g\u00f6ngy\u00f6legek \u00e9rt\u00e9kveszt\u00e9se \u00e9s annak vissza\u00edr\u00e1sa": { + "account_number": 289, + "root_type": "Asset" + } + } + }, + "SZ\u00c1MLAOSZT\u00c1LY K\u00d6VETEL\u00c9SEK, P\u00c9NZ\u00dcGYI ESZK\u00d6Z\u00d6K \u00c9S AKT\u00cdV ID\u0150BELI ELHAT\u00c1ROL\u00c1SOK": { + "account_number": 3, + "root_type": "Asset", + "is_group": 1, + "K\u00d6VETEL\u00c9SEK \u00c1RUSZ\u00c1LL\u00cdT\u00c1SB\u00d3L \u00c9S SZOLG\u00c1LTAT\u00c1SB\u00d3L (VEV\u0150K)": { + "account_number": 31, + "root_type": "Asset", + "is_group": 1, + "Vev\u0151k\u00f6vetel\u00e9sek": { + "account_number": 311, + "root_type": "Asset" + }, + "Vev\u0151k\u00f6vetel\u00e9sek \u00e9rt\u00e9kveszt\u00e9se": { + "account_number": 319, + "root_type": "Asset" + } + }, + "K\u00d6VETEL\u00c9SEK TULAJDONOSSAL SZEMBEN": { + "account_number": 33, + "root_type": "Asset", + "is_group": 1, + "Jegyzett, de m\u00e9g be nem fizetett t\u0151ke": { + "account_number": 331, + "root_type": "Asset" + } + }, + "V\u00c1LT\u00d3K\u00d6VETEL\u00c9SEK": { + "account_number": 34, + "root_type": "Asset", + "is_group": 1, + "V\u00e1lt\u00f3k\u00f6vetel\u00e9sek": { + "account_number": 341, + "root_type": "Asset" + }, + "V\u00e1lt\u00f3k\u00f6vetel\u00e9sek \u00e9rt\u00e9kveszt\u00e9se": { + "account_number": 349, + "root_type": "Asset" + } + }, + "ADOTT EL\u0150LEGEK": { + "account_number": 35, + "root_type": "Asset", + "is_group": 1, + "Immateri\u00e1lis javakra adott el\u0151legek": { + "account_number": 351, + "root_type": "Asset" + }, + "Beruh\u00e1z\u00e1sokra adott el\u0151legek": { + "account_number": 352, + "root_type": "Asset" + }, + "K\u00e9szletekre adott el\u0151legek": { + "account_number": 353, + "root_type": "Asset" + }, + "Egy\u00e9b c\u00e9lra adott el\u0151legek": { + "account_number": 354, + "root_type": "Asset" + }, + "Adott el\u0151legek \u00e9rt\u00e9kveszt\u00e9se": { + "account_number": 359, + "root_type": "Asset" + } + }, + "EGY\u00c9B K\u00d6VETEL\u00c9SEK": { + "account_number": 36, + "root_type": "Asset", + "is_group": 1, + "Munkav\u00e1llal\u00f3kkal szembeni k\u00f6vetel\u00e9sek": { + "account_number": 361, + "root_type": "Asset" + }, + "K\u00f6lts\u00e9gvet\u00e9ssel szembeni k\u00f6vetel\u00e9sek": { + "account_number": 362, + "root_type": "Asset" + }, + "R\u00f6vid lej\u00e1ratra k\u00f6lcs\u00f6nadott p\u00e9nzeszk\u00f6z\u00f6k": { + "account_number": 363, + "root_type": "Asset" + }, + "R\u00e9szesed\u00e9sekkel, \u00e9rt\u00e9kpap\u00edrokkal kapcsolatos k\u00f6vetel\u00e9sek": { + "account_number": 364, + "root_type": "Asset" + }, + "K\u00fcl\u00f6nf\u00e9le egy\u00e9b k\u00f6vetel\u00e9sek": { + "account_number": 365, + "root_type": "Asset" + }, + "Egy\u00e9b k\u00f6vetel\u00e9sek \u00e9rt\u00e9kveszt\u00e9se": { + "account_number": 369, + "root_type": "Asset" + } + }, + "\u00c9RT\u00c9KPAP\u00cdROK": { + "account_number": 37, + "root_type": "Asset", + "is_group": 1, + "R\u00e9szesed\u00e9sek": { + "account_number": 371, + "root_type": "Asset" + }, + "Saj\u00e1t r\u00e9szv\u00e9nyek, saj\u00e1t \u00fczletr\u00e9szek": { + "account_number": 372, + "root_type": "Asset" + }, + "Forgat\u00e1si c\u00e9l\u00fa hitelviszonyt megtestes\u00edt\u0151 \u00e9rt\u00e9kpap\u00edrok": { + "account_number": 373, + "root_type": "Asset" + }, + "\u00c9rt\u00e9kpap\u00edr elsz\u00e1mol\u00e1si sz\u00e1mla": { + "account_number": 378, + "root_type": "Asset" + }, + "\u00c9rt\u00e9kpap\u00edrok \u00e9rt\u00e9kveszt\u00e9se": { + "account_number": 379, + "root_type": "Asset" + } + }, + "P\u00c9NZESZK\u00d6Z\u00d6K": { + "account_number": 38, + "root_type": "Asset", + "is_group": 1, + "P\u00e9nzt\u00e1r": { + "account_number": 381, + "root_type": "Asset" + }, + "Valutap\u00e9nzt\u00e1r": { + "account_number": 382, + "root_type": "Asset" + }, + "Csekkek": { + "account_number": 383, + "root_type": "Asset" + }, + "Elsz\u00e1mol\u00e1si bet\u00e9tsz\u00e1mla": { + "account_number": 384, + "root_type": "Asset" + }, + "Elk\u00fcl\u00f6n\u00edtett bet\u00e9tsz\u00e1mla": { + "account_number": 385, + "root_type": "Asset" + }, + "Devizabet\u00e9tsz\u00e1mla": { + "account_number": 386, + "root_type": "Asset" + }, + "Elektronikus p\u00e9nz": { + "account_number": 387, + "root_type": "Asset" + }, + "\u00c1tvezet\u00e9si sz\u00e1mla": { + "account_number": 389, + "root_type": "Asset" + } + }, + "AKT\u00cdV ID\u0150BELI ELHAT\u00c1ROL\u00c1SOK": { + "account_number": 39, + "root_type": "Asset", + "is_group": 1, + "Akt\u00edv id\u0151beli elhat\u00e1rol\u00e1sok": { + "account_number": 391, + "root_type": "Asset" + } + } + }, + "SZ\u00c1MLAOSZT\u00c1LY FORR\u00c1SOK": { + "account_number": 4, + "root_type": "Liability", + "is_group": 1, + "SAJ\u00c1T T\u0150KE": { + "account_number": 41, + "root_type": "Liability", + "is_group": 1, + "account_type": "Equity", + "Jegyzett t\u0151ke": { + "account_number": 411, + "root_type": "Liability", + "account_type": "Equity" + }, + "T\u0151ketartal\u00e9k": { + "account_number": 412, + "root_type": "Liability", + "account_type": "Equity" + }, + "Eredm\u00e9nytartal\u00e9k": { + "account_number": 413, + "root_type": "Liability", + "account_type": "Equity" + }, + "Lek\u00f6t\u00f6tt tartal\u00e9k": { + "account_number": 414, + "root_type": "Liability", + "account_type": "Equity" + }, + "Ad\u00f3zott eredm\u00e9ny": { + "account_number": 419, + "root_type": "Liability", + "account_type": "Equity" + } + }, + "C\u00c9LTARTAL\u00c9KOK": { + "account_number": 42, + "root_type": "Liability", + "is_group": 1, + "C\u00e9ltartal\u00e9k a v\u00e1rhat\u00f3 k\u00f6telezetts\u00e9gekre": { + "account_number": 421, + "root_type": "Liability" + } + }, + "H\u00c1TRASOROLT K\u00d6TELEZETTS\u00c9GEK": { + "account_number": 43, + "root_type": "Liability", + "is_group": 1, + "H\u00e1trasorolt k\u00f6telezetts\u00e9gek": { + "account_number": 431, + "root_type": "Liability" + } + }, + "HOSSZ\u00da LEJ\u00c1RAT\u00da K\u00d6TELEZETTS\u00c9GEK": { + "account_number": 44, + "root_type": "Liability", + "is_group": 1, + "Hossz\u00fa lej\u00e1ratra kapott k\u00f6lcs\u00f6n\u00f6k": { + "account_number": 441, + "root_type": "Liability" + }, + "\u00c1tv\u00e1ltoztathat\u00f3 k\u00f6tv\u00e9nyek": { + "account_number": 442, + "root_type": "Liability" + }, + "Tartoz\u00e1sok k\u00f6tv\u00e9nykibocs\u00e1t\u00e1sb\u00f3l": { + "account_number": 443, + "root_type": "Liability" + }, + "Beruh\u00e1z\u00e1si \u00e9s fejleszt\u00e9si hitelek": { + "account_number": 444, + "root_type": "Liability" + }, + "Egy\u00e9b hossz\u00fa lej\u00e1rat\u00fa hitelek": { + "account_number": 445, + "root_type": "Liability" + }, + "P\u00e9nz\u00fcgyi l\u00edzing miatti k\u00f6telezetts\u00e9gek": { + "account_number": 446, + "root_type": "Liability" + }, + "Egy\u00e9b hossz\u00fa lej\u00e1rat\u00fa k\u00f6telezetts\u00e9gek": { + "account_number": 449, + "root_type": "Liability" + } + }, + "R\u00d6VID LEJ\u00c1RAT\u00da K\u00d6TELEZETTS\u00c9GEK": { + "account_number": "45-47", + "root_type": "Liability", + "is_group": 1, + "R\u00f6vid lej\u00e1rat\u00fa k\u00f6lcs\u00f6n\u00f6k": { + "account_number": 451, + "root_type": "Liability" + }, + "R\u00f6vid lej\u00e1rat\u00fa hitelek": { + "account_number": 452, + "root_type": "Liability" + }, + "Vev\u0151kt\u0151l kapott el\u0151legek": { + "account_number": 453, + "root_type": "Liability" + }, + "K\u00f6telezetts\u00e9gek \u00e1rusz\u00e1ll\u00edt\u00e1sb\u00f3l \u00e9s szolg\u00e1ltat\u00e1sb\u00f3l (sz\u00e1ll\u00edt\u00f3k)": { + "account_number": 454, + "root_type": "Liability" + }, + "Beruh\u00e1z\u00e1si sz\u00e1ll\u00edt\u00f3k": { + "account_number": 455, + "root_type": "Liability" + }, + "Nem sz\u00e1ml\u00e1zott sz\u00e1ll\u00edt\u00f3k": { + "account_number": 456, + "root_type": "Liability" + }, + "V\u00e1lt\u00f3tartoz\u00e1sok": { + "account_number": 457, + "root_type": "Liability" + }, + "Eredm\u00e9nyt terhel\u0151 ad\u00f3k elsz\u00e1mol\u00e1sa": { + "account_number": 461, + "root_type": "Liability" + }, + "Szem\u00e9lyi j\u00f6vedelemad\u00f3 (SZJA) elsz\u00e1mol\u00e1sa": { + "account_number": 462, + "root_type": "Liability" + }, + "J\u00f6ved\u00e9ki ad\u00f3 elsz\u00e1mol\u00e1sa": { + "account_number": 463, + "root_type": "Liability" + }, + "G\u00e9pj\u00e1rm\u0171 ad\u00f3 (c\u00e9gaut\u00f3ad\u00f3) elsz\u00e1mol\u00e1sa": { + "account_number": 464, + "root_type": "Liability" + }, + "V\u00e1melsz\u00e1mol\u00e1si sz\u00e1mla": { + "account_number": 465, + "root_type": "Liability" + }, + "El\u0151zetesen felsz\u00e1m\u00edtott \u00e1ltal\u00e1nos forgalmi ad\u00f3": { + "account_number": 466, + "root_type": "Liability" + }, + "Fizetend\u0151 \u00e1ltal\u00e1nos forgalmi ad\u00f3": { + "account_number": 467, + "root_type": "Liability" + }, + "\u00c1ltal\u00e1nos forgalmi ad\u00f3 elsz\u00e1mol\u00e1si sz\u00e1mla": { + "account_number": 468, + "root_type": "Liability" + }, + "Helyi ad\u00f3k elsz\u00e1mol\u00e1si sz\u00e1mla": { + "account_number": 469, + "root_type": "Liability" + }, + "J\u00f6vedelemelsz\u00e1mol\u00e1si sz\u00e1mla": { + "account_number": 471, + "root_type": "Liability" + }, + "Fel nem vett j\u00e1rand\u00f3s\u00e1gok": { + "account_number": 472, + "root_type": "Liability" + }, + "Szoci\u00e1lis hozz\u00e1j\u00e1rul\u00e1si ad\u00f3": { + "account_number": 473, + "root_type": "Liability" + }, + "Szakk\u00e9pz\u00e9si hozz\u00e1j\u00e1rul\u00e1s": { + "account_number": 474, + "root_type": "Liability" + }, + "Egy\u00e9b \u00e1llami ad\u00f3hat\u00f3s\u00e1ggal szembeni k\u00f6telezetts\u00e9g elsz\u00e1mol\u00e1sa": { + "account_number": 476, + "root_type": "Liability" + }, + "R\u00f6vid lej\u00e1rat\u00fa egy\u00e9b k\u00f6telezetts\u00e9gek munkav\u00e1llal\u00f3kkal \u00e9s tulajdonosokkal szemben": { + "account_number": 477, + "root_type": "Liability" + }, + "R\u00e9szesed\u00e9sekkel, \u00e9rt\u00e9kpap\u00edrokkal kapcsolatos k\u00f6telezetts\u00e9gek": { + "account_number": 478, + "root_type": "Liability" + }, + "K\u00fcl\u00f6nf\u00e9le egy\u00e9b r\u00f6vid lej\u00e1rat\u00fa k\u00f6telezetts\u00e9gek": { + "account_number": 479, + "root_type": "Liability" + } + }, + "PASSZ\u00cdV ID\u0150BELI ELHAT\u00c1ROL\u00c1SOK": { + "account_number": 48, + "root_type": "Liability", + "is_group": 1, + "Passz\u00edv id\u0151beli elhat\u00e1rol\u00e1sok": { + "account_number": 481, + "root_type": "Liability" + } + }, + "\u00c9VI M\u00c9RLEGSZ\u00c1ML\u00c1K": { + "account_number": 49, + "root_type": "Liability", + "is_group": 1, + "Nyit\u00f3m\u00e9rleg sz\u00e1mla": { + "account_number": 491, + "root_type": "Liability" + }, + "Z\u00e1r\u00f3m\u00e9rleg sz\u00e1mla": { + "account_number": 492, + "root_type": "Liability" + }, + "Ad\u00f3zott eredm\u00e9ny elsz\u00e1mol\u00e1si sz\u00e1mla": { + "account_number": 493, + "root_type": "Liability" + } + } + }, + "SZ\u00c1MLAOSZT\u00c1LY K\u00d6LTS\u00c9GNEMEK": { + "account_number": 5, + "root_type": "Expense", + "is_group": 1, + "ANYAGK\u00d6LTS\u00c9G": { + "account_number": 51, + "root_type": "Expense", + "is_group": 1, + "Alapanyagok k\u00f6lts\u00e9gei": { + "account_number": 511, + "root_type": "Expense" + }, + "Egy \u00e9ven bel\u00fcl elhaszn\u00e1l\u00f3d\u00f3 anyagi eszk\u00f6z\u00f6k k\u00f6lts\u00e9gei": { + "account_number": 512, + "root_type": "Expense" + }, + "Egy\u00e9b anyagk\u00f6lts\u00e9g": { + "account_number": 513, + "root_type": "Expense" + }, + "Anyagk\u00f6lts\u00e9g megt\u00e9r\u00fcl\u00e9s": { + "account_number": 519, + "root_type": "Expense" + } + }, + "IG\u00c9NYBE VETT SZOLG\u00c1LTAT\u00c1SOK K\u00d6LTS\u00c9GEI": { + "account_number": 52, + "root_type": "Expense", + "is_group": 1, + "Sz\u00e1ll\u00edt\u00e1srakod\u00e1s, rakt\u00e1roz\u00e1s k\u00f6lts\u00e9gei": { + "account_number": 521, + "root_type": "Expense" + }, + "B\u00e9rleti d\u00edjak": { + "account_number": 522, + "root_type": "Expense" + }, + "Karbantart\u00e1si k\u00f6lts\u00e9gek": { + "account_number": 523, + "root_type": "Expense" + }, + "Hirdet\u00e9s, rekl\u00e1m, propaganda k\u00f6lts\u00e9gek": { + "account_number": 524, + "root_type": "Expense" + }, + "Oktat\u00e1s \u00e9s tov\u00e1bbk\u00e9pz\u00e9s k\u00f6lts\u00e9gei": { + "account_number": 525, + "root_type": "Expense" + }, + "Utaz\u00e1si \u00e9s kik\u00fcldet\u00e9si k\u00f6lts\u00e9gek (napid\u00edj n\u00e9lk\u00fcl)": { + "account_number": 526, + "root_type": "Expense" + }, + "Ig\u00e9nybe vett egy\u00e9b szolg\u00e1ltat\u00e1sok k\u00f6lts\u00e9gei": { + "account_number": 529, + "root_type": "Expense" + } + }, + "EGY\u00c9B SZOLG\u00c1LTAT\u00c1SOK K\u00d6LTS\u00c9GEI": { + "account_number": 53, + "root_type": "Expense", + "is_group": 1, + "Hat\u00f3s\u00e1gi igazgat\u00e1si, szolg\u00e1ltat\u00e1si d\u00edjak, illet\u00e9kek": { + "account_number": 531, + "root_type": "Expense" + }, + "P\u00e9nz\u00fcgyi, befektet\u00e9si szolg\u00e1ltat\u00e1si d\u00edjak": { + "account_number": 532, + "root_type": "Expense" + }, + "Biztos\u00edt\u00e1si d\u00edjak": { + "account_number": 533, + "root_type": "Expense" + }, + "K\u00f6lts\u00e9gk\u00e9nt elsz\u00e1moland\u00f3 ad\u00f3k, j\u00e1rul\u00e9kok, term\u00e9kd\u00edjak": { + "account_number": 534, + "root_type": "Expense" + }, + "K\u00fcl\u00f6nf\u00e9le egy\u00e9b szolg\u00e1ltat\u00e1sok k\u00f6lts\u00e9gei": { + "account_number": 539, + "root_type": "Expense" + } + }, + "B\u00c9RK\u00d6LTS\u00c9G": { + "account_number": 54, + "root_type": "Expense", + "is_group": 1, + "B\u00e9rk\u00f6lts\u00e9g": { + "account_number": 541, + "root_type": "Expense" + } + }, + "SZEM\u00c9LYI JELLEG\u0170 EGY\u00c9B KIFIZET\u00c9SEK": { + "account_number": 55, + "root_type": "Expense", + "is_group": 1, + "Munkav\u00e1llal\u00f3knak, tagoknak fizetett szem\u00e9lyi jelleg\u0171 kifizet\u00e9sek": { + "account_number": 551, + "root_type": "Expense" + }, + "J\u00f3l\u00e9ti \u00e9s kultur\u00e1lis k\u00f6lts\u00e9gek": { + "account_number": 552, + "root_type": "Expense" + }, + "Reprezent\u00e1ci\u00f3s k\u00f6lts\u00e9gek": { + "account_number": 553, + "root_type": "Expense" + }, + "Egy\u00e9b szem\u00e9lyi jelleg\u0171 kifizet\u00e9sek": { + "account_number": 559, + "root_type": "Expense" + } + }, + "B\u00c9RJ\u00c1RUL\u00c9KOK": { + "account_number": 56, + "root_type": "Expense", + "is_group": 1, + "Szoci\u00e1lis hozz\u00e1j\u00e1rul\u00e1si ad\u00f3": { + "account_number": 561, + "root_type": "Expense" + }, + "Szakk\u00e9pz\u00e9si hozz\u00e1j\u00e1rul\u00e1s": { + "account_number": 563, + "root_type": "Expense" + }, + "Egy\u00e9b b\u00e9rj\u00e1rul\u00e9kok": { + "account_number": 569, + "root_type": "Expense" + } + }, + "\u00c9RT\u00c9KCS\u00d6KKEN\u00c9SI LE\u00cdR\u00c1S": { + "account_number": 57, + "root_type": "Expense", + "is_group": 1, + "Terv szerinti \u00e9rt\u00e9kcs\u00f6kken\u00e9si le\u00edr\u00e1s": { + "account_number": 571, + "root_type": "Expense" + }, + "Kis \u00e9rt\u00e9k\u0171 eszk\u00f6z\u00f6k egy \u00f6sszegben elsz\u00e1molt \u00e9rt\u00e9kcs\u00f6kken\u00e9si le\u00edr\u00e1sa": { + "account_number": 572, + "root_type": "Expense" + } + }, + "AKTIV\u00c1LT SAJ\u00c1T TELJES\u00cdTM\u00c9NYEK \u00c9RT\u00c9KE": { + "account_number": 58, + "root_type": "Expense", + "is_group": 1, + "Saj\u00e1t termel\u00e9s\u0171 k\u00e9szletek \u00e1llom\u00e1nyv\u00e1ltoz\u00e1sa": { + "account_number": 581, + "root_type": "Expense" + }, + "Saj\u00e1t el\u0151\u00e1ll\u00edt\u00e1s\u00fa eszk\u00f6z\u00f6k aktiv\u00e1lt \u00e9rt\u00e9ke": { + "account_number": 582, + "root_type": "Expense" + }, + "Aktiv\u00e1lt saj\u00e1t teljes\u00edtm\u00e9nyek \u00e1tvezet\u00e9si sz\u00e1mla": { + "account_number": 589, + "root_type": "Expense" + } + }, + "K\u00d6LTS\u00c9GNEMEK \u00c1TVEZET\u00c9SE": { + "account_number": 59, + "root_type": "Expense", + "is_group": 1, + "K\u00f6lts\u00e9gnemek \u00e1tvezet\u00e9si sz\u00e1mla": { + "account_number": 599, + "root_type": "Expense" + } + } + }, + "SZ\u00c1MLAOSZT\u00c1LY R\u00c1FORD\u00cdT\u00c1SOK": { + "account_number": 8, + "root_type": "Expense", + "is_group": 1, + "ANYAGJELLEG\u0170 R\u00c1FORD\u00cdT\u00c1SOK": { + "account_number": 81, + "root_type": "Expense", + "is_group": 1, + "Anyagk\u00f6lts\u00e9g": { + "account_number": 811, + "root_type": "Expense" + }, + "Ig\u00e9nybe vett szolg\u00e1ltat\u00e1sok \u00e9rt\u00e9ke": { + "account_number": 812, + "root_type": "Expense" + }, + "Egy\u00e9b szolg\u00e1ltat\u00e1sok \u00e9rt\u00e9ke": { + "account_number": 813, + "root_type": "Expense" + }, + "Eladott \u00e1ruk beszerz\u00e9si \u00e9rt\u00e9ke": { + "account_number": 814, + "root_type": "Expense" + }, + "Eladott (k\u00f6zvet\u00edtett) szolg\u00e1ltat\u00e1sok \u00e9rt\u00e9ke": { + "account_number": 815, + "root_type": "Expense" + } + }, + "SZEM\u00c9LYI JELLEG\u0170 R\u00c1FORD\u00cdT\u00c1SOK": { + "account_number": 82, + "root_type": "Expense", + "is_group": 1, + "B\u00e9rk\u00f6lts\u00e9g": { + "account_number": 821, + "root_type": "Expense" + }, + "Szem\u00e9lyi jelleg\u0171 egy\u00e9b kifizet\u00e9sek": { + "account_number": 822, + "root_type": "Expense" + }, + "B\u00e9rj\u00e1rul\u00e9kok": { + "account_number": 823, + "root_type": "Expense" + } + }, + "\u00c9RT\u00c9KCS\u00d6KKEN\u00c9SI LE\u00cdR\u00c1S": { + "account_number": 83, + "root_type": "Expense", + "is_group": 1, + "\u00c9rt\u00e9kcs\u00f6kken\u00e9si le\u00edr\u00e1s": { + "account_number": 831, + "root_type": "Expense" + } + }, + "EGY\u00c9B R\u00c1FORD\u00cdT\u00c1SOK": { + "account_number": 86, + "root_type": "Expense", + "is_group": 1, + "Egy\u00e9b r\u00e1ford\u00edt\u00e1snak min\u0151s\u00fcl\u0151 \u00e9rt\u00e9kes\u00edt\u00e9sek": { + "account_number": 861, + "root_type": "Expense", + "is_group": 1, + "\u00c9rt\u00e9kes\u00edtett immateri\u00e1lis javak, t\u00e1rgyi eszk\u00f6z\u00f6k k\u00f6nyv szerinti \u00e9rt\u00e9ke": { + "account_number": 8611, + "root_type": "Expense" + }, + "\u00c9rt\u00e9kes\u00edtett, \u00e1truh\u00e1zott (engedm\u00e9nyezett) k\u00f6vetel\u00e9sek k\u00f6nyv szerinti \u00e9rt\u00e9ke": { + "account_number": 8612, + "root_type": "Expense" + } + }, + "Egy\u00e9b r\u00e1ford\u00edt\u00e1snak min\u0151s\u00fcl\u0151 eszk\u00f6z kivezet\u00e9sek": { + "account_number": 862, + "root_type": "Expense", + "is_group": 1, + "Hi\u00e1nyz\u00f3, megsemmis\u00fclt, kiselejtezett immateri\u00e1lis javak, t\u00e1rgyi eszk\u00f6z\u00f6k nett\u00f3 \u00e9rt\u00e9ke": { + "account_number": 8621, + "root_type": "Expense" + }, + "Hi\u00e1nyz\u00f3, megsemmis\u00fclt, \u00e1llom\u00e1nyb\u00f3l kivezetett k\u00e9szletek k\u00f6nyv szerinti \u00e9rt\u00e9ke": { + "account_number": 8622, + "root_type": "Expense" + } + }, + "Behajthatatlan k\u00f6vetel\u00e9sek le\u00edrt \u00f6sszege": { + "account_number": 863, + "root_type": "Expense" + }, + "C\u00e9ltartal\u00e9k k\u00e9pz\u00e9s": { + "account_number": 864, + "root_type": "Expense" + }, + "Ut\u00f3lag adott, nem sz\u00e1ml\u00e1zott engedm\u00e9ny": { + "account_number": 865, + "root_type": "Expense" + }, + "Egy\u00e9b r\u00e1ford\u00edt\u00e1sk\u00e9nt elsz\u00e1molt ad\u00f3k, illet\u00e9kek, hozz\u00e1j\u00e1rul\u00e1sok": { + "account_number": 866, + "root_type": "Expense", + "is_group": 1, + "K\u00f6zponti k\u00f6lts\u00e9gvet\u00e9ssel elsz\u00e1molt ad\u00f3k, illet\u00e9kek, hozz\u00e1j\u00e1rul\u00e1sok": { + "account_number": 8661, + "root_type": "Expense" + }, + "Helyi \u00f6nkorm\u00e1nyzatokkal elsz\u00e1molt ad\u00f3k, illet\u00e9kek, hozz\u00e1j\u00e1rul\u00e1sok": { + "account_number": 8662, + "root_type": "Expense" + }, + "Elk\u00fcl\u00f6n\u00edtett \u00e1llami p\u00e9nzalapokkal elsz\u00e1molt ad\u00f3k, illet\u00e9kek, hozz\u00e1j\u00e1rul\u00e1sok": { + "account_number": 8663, + "root_type": "Expense" + }, + "T\u00e1rsadalombiztos\u00edt\u00e1ssal elsz\u00e1molt ad\u00f3k, illet\u00e9kek, hozz\u00e1j\u00e1rul\u00e1sok": { + "account_number": 8664, + "root_type": "Expense" + }, + "EU p\u00e9nz\u00fcgyi alapokkal elsz\u00e1molt ad\u00f3k, illet\u00e9kek, hozz\u00e1j\u00e1rul\u00e1sok": { + "account_number": 8665, + "root_type": "Expense" + }, + "R\u00e1ford\u00edt\u00e1sk\u00e9nt elsz\u00e1molt egy\u00e9b ad\u00f3k \u00e9s ad\u00f3jelleg\u0171 t\u00e9telek": { + "account_number": 8666, + "root_type": "Expense" + } + }, + "Egy\u00e9b r\u00e1ford\u00edt\u00e1sk\u00e9nt elsz\u00e1molt, ad\u00f3nak nem min\u0151s\u00fcl\u0151 kifizet\u00e9sek": { + "account_number": 867, + "root_type": "Expense", + "is_group": 1, + "K\u00e1resem\u00e9nnyel kapcsolatos fizetett, fizetend\u0151 \u00f6sszegek": { + "account_number": 8671, + "root_type": "Expense" + }, + "K\u00f6lts\u00e9gek (r\u00e1f.) ellent\u00e9telez\u00e9s\u00e9re visszafizet\u00e9si k\u00f6telezetts\u00e9g n\u00e9lk\u00fcl adott t\u00e1mogat\u00e1s, v\u00e9glegesen \u00e1tadott p\u00e9nzeszk\u00f6z, juttat\u00e1s": { + "account_number": 8672, + "root_type": "Expense" + }, + "Fejleszt\u00e9si c\u00e9lra, visszafizet\u00e9si k\u00f6telezetts\u00e9g n\u00e9lk\u00fcl adott t\u00e1mogat\u00e1s, juttat\u00e1s": { + "account_number": 8673, + "root_type": "Expense" + }, + "Fejleszt\u00e9si c\u00e9lra kapott t\u00e1mogat\u00e1s visszafizetend\u0151 \u00f6sszege": { + "account_number": 8674, + "root_type": "Expense" + }, + "Tao \u00e1ltal elismert b\u00edrs\u00e1gok, k\u00f6tb\u00e9rek, k\u00e9sedelmi kamatok, p\u00f3tl\u00e9kok, k\u00e1rt\u00e9r\u00edt\u00e9sek, s\u00e9relemd\u00edjak": { + "account_number": 8675, + "root_type": "Expense" + }, + "Tao \u00e1ltal el nem ismert b\u00edrs\u00e1gok, k\u00f6tb\u00e9rek, k\u00e9sedelmi kamatok, p\u00f3tl\u00e9kok, k\u00e1rt\u00e9r\u00edt\u00e9sek, s\u00e9relemd\u00edjak": { + "account_number": 8676, + "root_type": "Expense" + } + }, + "Terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9sek, \u00e9rt\u00e9kveszt\u00e9sek": { + "account_number": 868, + "root_type": "Expense", + "is_group": 1, + "Immateri\u00e1lis javak terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 8681, + "root_type": "Expense" + }, + "T\u00e1rgyi eszk\u00f6z\u00f6k terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9se": { + "account_number": 8682, + "root_type": "Expense" + }, + "K\u00e9szletek \u00e9rt\u00e9kveszt\u00e9se": { + "account_number": 8683, + "root_type": "Expense" + }, + "K\u00f6vetel\u00e9sek \u00e9rt\u00e9kveszt\u00e9se": { + "account_number": 8684, + "root_type": "Expense" + } + }, + "K\u00fcl\u00f6nf\u00e9le egy\u00e9b r\u00e1ford\u00edt\u00e1sok": { + "account_number": 869, + "root_type": "Expense", + "is_group": 1, + "T\u00e1rsas\u00e1gba bevitt, \u00e9rt\u00e9kp. v. r\u00e9sz. nem min\u0151s\u00fcl\u0151 vagyont. k\u00f6nyv sz. \u00e9s l\u00e9tes\u00edt\u0151 o. meghat. \u00e9rt\u00e9k\u00e9nek vesztes\u00e9gjelleg\u0171 k\u00fcl.": { + "account_number": 8691, + "root_type": "Expense" + }, + "Ellent\u00e9telez\u00e9s n\u00e9lk\u00fcl \u00e1tv\u00e1llalt k\u00f6telezetts\u00e9g szerz\u0151d\u00e9s szerinti \u00f6sszege": { + "account_number": 8692, + "root_type": "Expense" + }, + "T\u00e9r\u00edt\u00e9s n\u00e9lk\u00fcl \u00e1tadott, r\u00e9szesed\u00e9snek vagy \u00e9rt\u00e9kpap\u00edrnak nem min\u0151s\u00fcl\u0151 eszk\u00f6z\u00f6k nyilv\u00e1ntart\u00e1s szerinti \u00e9rt\u00e9ke": { + "account_number": 8693, + "root_type": "Expense" + }, + "T\u00e9r\u00edt\u00e9s n\u00e9lk\u00fcl ny\u00fajtott szolg\u00e1ltat\u00e1sok beker\u00fcl\u00e9si \u00e9rt\u00e9ke": { + "account_number": 8694, + "root_type": "Expense" + }, + "Elengedett k\u00f6vetel\u00e9sek k\u00f6nyv szerinti \u00e9rt\u00e9ke": { + "account_number": 8695, + "root_type": "Expense" + }, + "Egy\u00e9b, vagyoncs\u00f6kken\u00e9ssel j\u00e1r\u00f3 r\u00e1ford\u00edt\u00e1sok": { + "account_number": 8696, + "root_type": "Expense" + } + } + }, + "P\u00c9NZ\u00dcGYI M\u0170VELETEK R\u00c1FORD\u00cdT\u00c1SAI": { + "account_number": 87, + "root_type": "Expense", + "is_group": 1, + "R\u00e9szesed\u00e9sekb\u0151l sz\u00e1rmaz\u00f3 r\u00e1ford\u00edt\u00e1sok, \u00e1rfolyamvesztes\u00e9gek": { + "account_number": 871, + "root_type": "Expense" + }, + "Befektetett p\u00e9nz\u00fcgyi eszk\u00f6z\u00f6kb\u0151l (\u00e9rt\u00e9kpap\u00edrokb\u00f3l, k\u00f6lcs\u00f6n\u00f6kb\u0151l) sz\u00e1rmaz\u00f3 r\u00e1ford\u00edt\u00e1sok \u00e1rfolyamvesztes\u00e9gek": { + "account_number": 872, + "root_type": "Expense" + }, + "Hitelint\u00e9zetnek fizetend\u0151 kamatok \u00e9s kamatjelleg\u0171 r\u00e1ford\u00edt\u00e1sok": { + "account_number": 873, + "root_type": "Expense" + }, + "Nem hitelint\u00e9zetnek fizetend\u0151 kamatok \u00e9s kamatjelleg\u0171 r\u00e1ford\u00edt\u00e1sok": { + "account_number": 874, + "root_type": "Expense" + }, + "R\u00e9szesed\u00e9sek, \u00e9rt\u00e9kpap\u00edrok, bankbet\u00e9tek \u00e9rt\u00e9kveszt\u00e9se \u00e9s vissza\u00edr\u00e1sa": { + "account_number": 875, + "root_type": "Expense", + "is_group": 1, + "R\u00e9szesed\u00e9sek, \u00e9rt\u00e9kpap\u00edrok, bankbet\u00e9tek \u00e9rt\u00e9kveszt\u00e9se": { + "account_number": 8751, + "root_type": "Expense" + }, + "R\u00e9szesed\u00e9sek, \u00e9rt\u00e9kpap\u00edrok, bankbet\u00e9tek \u00e9rt\u00e9kveszt\u00e9s\u00e9nek vissza\u00edr\u00e1sa": { + "account_number": 8752, + "root_type": "Expense" + } + }, + "K\u00fclf\u00f6ldi p\u00e9nz\u00e9rt\u00e9kre sz\u00f3l\u00f3 eszk\u00f6z\u00f6k \u00e9s k\u00f6telezetts\u00e9gek \u00e1rfolyamvesztes\u00e9gei": { + "account_number": 876, + "root_type": "Expense", + "is_group": 1, + "Deviza \u00e9s valutak\u00e9szletek forintra \u00e1tv\u00e1lt\u00e1s\u00e1nak \u00e1rfolyamvesztes\u00e9ge": { + "account_number": 8761, + "root_type": "Expense" + }, + "K\u00fclf\u00f6ldi p\u00e9nz\u00e9rt\u00e9kre sz\u00f3l\u00f3 eszk\u00f6z\u00f6k \u00e9s k\u00f6telezetts\u00e9gek p\u00e9nz\u00fcgyileg rendezett \u00e1rfolyamvesztes\u00e9ge": { + "account_number": 8762, + "root_type": "Expense" + } + }, + "P\u00e9nz\u00fcgyi m\u0171veletek egy\u00e9b r\u00e1ford\u00edt\u00e1sai": { + "account_number": 877, + "root_type": "Expense" + }, + "P\u00e9nz\u00fcgyi rendez\u00e9shez kapcsol\u00f3d\u00f3an adott engedm\u00e9ny": { + "account_number": 878, + "root_type": "Expense" + }, + "Egy\u00e9b vagyoncs\u00f6kken\u00e9ssel j\u00e1r\u00f3 p\u00e9nz\u00fcgyi r\u00e1ford\u00edt\u00e1sok": { + "account_number": 879, + "root_type": "Expense" + } + }, + "EREDM\u00c9NYT TERHEL\u0150 AD\u00d3K": { + "account_number": 89, + "root_type": "Expense", + "is_group": 1, + "T\u00e1rsas\u00e1gi ad\u00f3": { + "account_number": 891, + "root_type": "Expense" + }, + "Kisv\u00e1llalati ad\u00f3": { + "account_number": 893, + "root_type": "Expense" + }, + "Eredm\u00e9nyt terhel\u0151 egy\u00e9b ad\u00f3k": { + "account_number": 899, + "root_type": "Expense" + } + } + }, + "SZ\u00c1MLAOSZT\u00c1LY BEV\u00c9TELEK": { + "account_number": 9, + "root_type": "Income", + "is_group": 1, + "BELF\u00d6LDI \u00c9RT\u00c9KES\u00cdT\u00c9S \u00c1RBEV\u00c9TELE": { + "account_number": 91, + "root_type": "Income", + "is_group": 1, + "Belf\u00f6ldinek \u00e9rt\u00e9kes\u00edtett saj\u00e1t termel\u00e9s\u0171 k\u00e9szletek \u00e1rbev\u00e9tele": { + "account_number": 911, + "root_type": "Income" + }, + "Belf\u00f6ldinek \u00e9rt\u00e9kes\u00edtett v\u00e1s\u00e1rolt k\u00e9szletek \u00e1rbev\u00e9tele": { + "account_number": 912, + "root_type": "Income" + }, + "Belf\u00f6ldinek ny\u00fajtott szolg\u00e1ltat\u00e1sok \u00e1rbev\u00e9tele": { + "account_number": 913, + "root_type": "Income" + }, + "Belf\u00f6ldi \u00e9rt\u00e9kes\u00edt\u00e9ssel kapcsolatos \u00e1rt\u00e1mogat\u00e1s": { + "account_number": 918, + "root_type": "Income" + }, + "Egy\u00e9b belf\u00f6ldi \u00e9rt\u00e9kes\u00edt\u00e9s \u00e1rbev\u00e9tele": { + "account_number": 919, + "root_type": "Income" + } + }, + "EXPORT\u00c9RT\u00c9KES\u00cdT\u00c9S \u00c1RBEV\u00c9TELE": { + "account_number": 92, + "root_type": "Income", + "is_group": 1, + "K\u00fclf\u00f6ldinek \u00e9rt\u00e9kes\u00edtett saj\u00e1t termel\u00e9s\u0171 k\u00e9szletek \u00e1rbev\u00e9tele": { + "account_number": 921, + "root_type": "Income" + }, + "K\u00fclf\u00f6ldinek \u00e9rt\u00e9kes\u00edtett v\u00e1s\u00e1rolt k\u00e9szletek \u00e1rbev\u00e9tele": { + "account_number": 922, + "root_type": "Income" + }, + "K\u00fclf\u00f6ldinek ny\u00fajtott szolg\u00e1ltat\u00e1sok \u00e1rbev\u00e9tele": { + "account_number": 923, + "root_type": "Income" + }, + "K\u00fclf\u00f6ldi \u00e9rt\u00e9kes\u00edt\u00e9ssel kapcsolatos \u00e1rt\u00e1mogat\u00e1s": { + "account_number": 928, + "root_type": "Income" + }, + "Egy\u00e9b k\u00fclf\u00f6ldi \u00e9rt\u00e9kes\u00edt\u00e9s \u00e1rbev\u00e9tele": { + "account_number": 929, + "root_type": "Income" + } + }, + "EGY\u00c9B BEV\u00c9TELEK": { + "account_number": 96, + "root_type": "Income", + "is_group": 1, + "Egy\u00e9b bev\u00e9telnek min\u0151s\u00fcl\u0151 \u00e9rt\u00e9kes\u00edt\u00e9sek": { + "account_number": 961, + "root_type": "Income", + "is_group": 1, + "\u00c9rt\u00e9kes\u00edtett immateri\u00e1lis javak, t\u00e1rgyi eszk\u00f6z\u00f6k ellen\u00e9rt\u00e9ke": { + "account_number": 9611, + "root_type": "Income" + }, + "\u00c9rt\u00e9kes\u00edtett, \u00e1truh\u00e1zott (engedm\u00e9nyezett) k\u00f6vetel\u00e9sek ellen\u00e9rt\u00e9ke": { + "account_number": 9612, + "root_type": "Income" + } + }, + "K\u00f6vetel\u00e9s k\u00f6nyv szerinti \u00e9rt\u00e9k\u00e9t meghalad\u00f3an realiz\u00e1lt \u00f6sszeg": { + "account_number": 963, + "root_type": "Income" + }, + "C\u00e9ltartal\u00e9k felold\u00e1s": { + "account_number": 964, + "root_type": "Income" + }, + "Ut\u00f3lag kapott, nem sz\u00e1ml\u00e1zott engedm\u00e9ny": { + "account_number": 965, + "root_type": "Income" + }, + "M\u0171k\u00f6d\u00e9si c\u00e9lra kapott t\u00e1mogat\u00e1s, juttat\u00e1s": { + "account_number": 966, + "root_type": "Income", + "is_group": 1, + "K\u00f6zponti k\u00f6lts\u00e9gvet\u00e9sb\u0151l kapott t\u00e1mogat\u00e1s, juttat\u00e1s": { + "account_number": 9661, + "root_type": "Income" + }, + "Helyi \u00f6nkorm\u00e1nyzatokt\u00f3l kapott t\u00e1mogat\u00e1s, juttat\u00e1s": { + "account_number": 9662, + "root_type": "Income" + }, + "Eur\u00f3pai Uni\u00f3t\u00f3l kapott t\u00e1mogat\u00e1s, juttat\u00e1s": { + "account_number": 9663, + "root_type": "Income" + }, + "Egy\u00e9b forr\u00e1sb\u00f3l kapott t\u00e1mogat\u00e1s, juttat\u00e1s": { + "account_number": 9664, + "root_type": "Income" + } + }, + "Egy\u00e9b bev\u00e9telk\u00e9nt elsz\u00e1molt p\u00e9nzbev\u00e9telek": { + "account_number": 967, + "root_type": "Income", + "is_group": 1, + "K\u00e1resem\u00e9nnyel kapcsolatos t\u00e9r\u00edt\u00e9sek": { + "account_number": 9671, + "root_type": "Income" + }, + "K\u00f6lts\u00e9gek (r\u00e1ford\u00edt\u00e1sok) ellent\u00e9telez\u00e9s\u00e9re kapott t\u00e1mogat\u00e1s, juttat\u00e1s": { + "account_number": 9672, + "root_type": "Income" + }, + "Fejleszt\u00e9si c\u00e9lra kapott t\u00e1mogat\u00e1s, juttat\u00e1s": { + "account_number": 9673, + "root_type": "Income" + }, + "Kapott b\u00edrs\u00e1gok, k\u00f6tb\u00e9rek, fekb\u00e9rek, k\u00e9sedelmi kamatok, k\u00e1rt\u00e9r\u00edt\u00e9sek": { + "account_number": 9674, + "root_type": "Income" + } + }, + "Terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9sek, \u00e9rt\u00e9kveszt\u00e9sek vissza\u00edr\u00e1sa": { + "account_number": 968, + "root_type": "Income", + "is_group": 1, + "Immateri\u00e1lis javak terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9s\u00e9nek vissza\u00edr\u00e1sa": { + "account_number": 9681, + "root_type": "Income" + }, + "T\u00e1rgyi eszk\u00f6z\u00f6k terven fel\u00fcli \u00e9rt\u00e9kcs\u00f6kken\u00e9s\u00e9nek vissza\u00edr\u00e1sa": { + "account_number": 9682, + "root_type": "Income" + }, + "K\u00e9szletek \u00e9rt\u00e9kveszt\u00e9s\u00e9nek vissza\u00edr\u00e1sa": { + "account_number": 9683, + "root_type": "Income" + }, + "R\u00e9szesed\u00e9sek \u00e9rt\u00e9kveszt\u00e9s\u00e9nek vissza\u00edr\u00e1sa": { + "account_number": 9684, + "root_type": "Income" + } + }, + "K\u00fcl\u00f6nf\u00e9le egy\u00e9b bev\u00e9telek": { + "account_number": 969, + "root_type": "Income", + "is_group": 1, + "Gazd. t\u00e1rs. bevitt, \u00e9rt\u00e9kp. v. r\u00e9sz. nem min\u0151s\u00fcl\u0151 vagyont. nyilv. sz. \u00e9rt. \u00e9s l\u00e9tes\u00edt\u0151 o. meghat. \u00e9rt. nyeres\u00e9gjelleg\u0171 k\u00fcl.": { + "account_number": 9691, + "root_type": "Income" + }, + "El\u00e9v\u00fclt k\u00f6telezetts\u00e9g k\u00f6nyv szerinti \u00e9rt\u00e9ke": { + "account_number": 9692, + "root_type": "Income" + }, + "T\u00e9r\u00edt\u00e9s n\u00e9lk\u00fcl \u00e1tvett, aj\u00e1nd\u00e9kk\u00e9nt, hagyat\u00e9kk\u00e9nt kapott, fellelt eszk\u00f6z\u00f6k piaci vagy jogszab\u00e1ly szerinti \u00e9rt\u00e9ke": { + "account_number": 9693, + "root_type": "Income" + }, + "T\u00e9r\u00edt\u00e9s n\u00e9lk\u00fcl kapott szolg\u00e1ltat\u00e1sok piaci vagy jogszab\u00e1ly szerinti \u00e9rt\u00e9ke": { + "account_number": 9694, + "root_type": "Income" + }, + "Elengedett \u00e9s ellent\u00e9telez\u00e9s n\u00e9lk\u00fcl \u00e1tv\u00e1llalt k\u00f6telezetts\u00e9g \u00e9rt\u00e9ke": { + "account_number": 9695, + "root_type": "Income" + }, + "Egy\u00e9b, vagyonn\u00f6veked\u00e9ssel j\u00e1r\u00f3 bev\u00e9telek": { + "account_number": 9696, + "root_type": "Income" + } + } + }, + "P\u00c9NZ\u00dcGYI M\u0170VELETEK BEV\u00c9TELEI": { + "account_number": 97, + "root_type": "Income", + "is_group": 1, + "R\u00e9szesed\u00e9sekb\u0151l sz\u00e1rmaz\u00f3 bev\u00e9telek, \u00e1rfolyamnyeres\u00e9gek": { + "account_number": 971, + "root_type": "Income" + }, + "Befektetett p\u00e9nz\u00fcgyi eszk\u00f6z\u00f6kb\u0151l (\u00e9rt\u00e9kpap\u00edrokb\u00f3l, k\u00f6lcs\u00f6n\u00f6kb\u0151l) sz\u00e1rmaz\u00f3 bev\u00e9telek, \u00e1rfolyamnyeres\u00e9gek": { + "account_number": 972, + "root_type": "Income" + }, + "Hitelint\u00e9zett\u0151l kapott kamatok \u00e9s kamatjelleg\u0171 bev\u00e9telek": { + "account_number": 973, + "root_type": "Income" + }, + "Nem hitelint\u00e9zett\u0151l kapott kamatok \u00e9s kamatjelleg\u0171 bev\u00e9telek": { + "account_number": 974, + "root_type": "Income" + }, + "Kapott (j\u00e1r\u00f3) osztal\u00e9k \u00e9s r\u00e9szesed\u00e9s": { + "account_number": 975, + "root_type": "Income" + }, + "K\u00fclf\u00f6ldi p\u00e9nz\u00e9rt\u00e9kre sz\u00f3l\u00f3 eszk\u00f6z\u00f6k \u00e9s k\u00f6telezetts\u00e9gek \u00e1rfolyamnyeres\u00e9gei": { + "account_number": 976, + "root_type": "Income", + "is_group": 1, + "Deviza \u00e9s valutak\u00e9szletek forintra \u00e1tv\u00e1lt\u00e1s\u00e1nak \u00e1rfolyamnyeres\u00e9ge": { + "account_number": 9761, + "root_type": "Income" + }, + "K\u00fclf\u00f6ldi p\u00e9nz\u00e9rt\u00e9kre sz\u00f3l\u00f3 eszk\u00f6z\u00f6k \u00e9s k\u00f6telezetts\u00e9gek p\u00e9nz\u00fcgyileg rendezett \u00e1rfolyamnyeres\u00e9ge": { + "account_number": 9762, + "root_type": "Income" + } + }, + "P\u00e9nz\u00fcgyi m\u0171veletek egy\u00e9b bev\u00e9telei": { + "account_number": 977, + "root_type": "Income" + }, + "P\u00e9nz\u00fcgyi rendez\u00e9shez kapcsol\u00f3d\u00f3an kapott engedm\u00e9ny": { + "account_number": 978, + "root_type": "Income" + }, + "Egy\u00e9b vagyonn\u00f6veked\u00e9ssel j\u00e1r\u00f3 p\u00e9nz\u00fcgyi bev\u00e9telek": { + "account_number": 979, + "root_type": "Income" + } + } + } + } +} \ No newline at end of file From 2ca0cf6fc4216cca086d81290b930b07bd454f24 Mon Sep 17 00:00:00 2001 From: niralisatapara Date: Wed, 2 Nov 2022 12:19:51 +0530 Subject: [PATCH 0448/1047] feat: item wise tds calculation --- .../purchase_invoice/purchase_invoice.json | 22 +++- .../purchase_invoice/test_purchase_invoice.py | 109 ------------------ .../tax_withholding_category.py | 19 +-- .../test_tax_withholding_category.py | 42 ++++++- erpnext/controllers/taxes_and_totals.py | 8 +- erpnext/patches.txt | 1 + erpnext/patches/v14_0/update_tds_fields.py | 25 ++++ erpnext/public/js/controllers/transaction.js | 4 +- 8 files changed, 107 insertions(+), 123 deletions(-) create mode 100644 erpnext/patches/v14_0/update_tds_fields.py diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 25b128b893..2f9ee97aa9 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -57,6 +57,8 @@ "column_break_28", "total", "net_total", + "tax_withholding_net_total", + "base_tax_withholding_net_total", "taxes_section", "taxes_and_charges", "column_break_58", @@ -1421,6 +1423,24 @@ "label": "Is Old Subcontracting Flow", "read_only": 1 }, + { + "default": "0", + "fieldname": "tax_withholding_net_total", + "fieldtype": "Currency", + "label": "Tax Withholding Net Total", + "no_copy": 1, + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "base_tax_withholding_net_total", + "fieldtype": "Currency", + "label": "Base Tax Withholding Net Total", + "no_copy": 1, + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, { "collapsible_depends_on": "tax_withheld_vouchers", "fieldname": "tax_withheld_vouchers_section", @@ -1583,4 +1603,4 @@ "timeline_field": "supplier", "title_field": "title", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 58e29f1a61..76ea95528c 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1574,35 +1574,6 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): self.assertTrue(return_pi.docstatus == 1) - def test_without_tds(self): - make_purchase_invoice_tds() - - def test_total_tds(self): - supplier = create_supplier( - supplier_name="_Test TDS Advance Supplier", - tax_withholding_category="TDS - 194 - Dividends - Individual", - ) - pi = make_purchase_invoice_tds(supplier= "_Test TDS Advance Supplier",total_tds = 1) - - sum_tds = 0 - for item in pi.items: - sum_tds += item.net_amount - - self.assertEqual(pi.tax_withholding_net_total, sum_tds) - for tax in pi.taxes: - self.assertEqual(tax.tax_amount, pi.tax_withholding_net_total * 0.10) - - def test_partial_tds(self): - pi = make_purchase_invoice_tds(supplier= "_Test TDS Advance Supplier",partial_tds = 1) - - sum_tds = 0 - for item in pi.items: - if item.apply_tds: - sum_tds += item.net_amount - - self.assertEqual(pi.tax_withholding_net_total, sum_tds) - for tax in pi.taxes: - self.assertEqual(tax.tax_amount, pi.tax_withholding_net_total * 0.10) def check_gl_entries(doc, voucher_no, expected_gle, posting_date): gl_entries = frappe.db.sql( @@ -1711,86 +1682,6 @@ def make_purchase_invoice(**args): pi.submit() return pi -def make_purchase_invoice_tds(**args): - pi = frappe.new_doc("Purchase Invoice") - args = frappe._dict(args) - pi.posting_date = args.posting_date or today() - if args.posting_time: - pi.posting_time = args.posting_time - if args.update_stock: - pi.update_stock = 1 - if args.is_paid: - pi.is_paid = 1 - - if args.cash_bank_account: - pi.cash_bank_account = args.cash_bank_account - - pi.company = args.company or "_Test Company" - pi.supplier = args.supplier or "_Test Supplier" - pi.currency = args.currency or "INR" - pi.conversion_rate = args.conversion_rate or 1 - pi.is_return = args.is_return - pi.return_against = args.return_against - pi.is_subcontracted = args.is_subcontracted or 0 - pi.supplier_warehouse = args.supplier_warehouse or "_Test Warehouse 1 - _TC" - pi.cost_center = args.parent_cost_center - - if args.total_tds or args.partial_tds: - pi.apply_tds = 1 - - pi.extend( - "items", - [ - { - "item_code": args.item or args.item_code or "_Test Item", - "warehouse": args.warehouse or "_Test Warehouse - _TC", - "qty": args.qty or 5, - "received_qty": args.received_qty or 0, - "rejected_qty": args.rejected_qty or 0, - "rate": args.rate or 5000, - "price_list_rate": args.price_list_rate or 5000, - "expense_account": args.expense_account or "_Test Account Cost for Goods Sold - _TC", - "discount_account": args.discount_account or None, - "discount_amount": args.discount_amount or 0, - "conversion_factor": 1.0, - "serial_no": args.serial_no, - "stock_uom": args.uom or "_Test UOM", - "cost_center": args.cost_center or "_Test Cost Center - _TC", - "project": args.project, - "rejected_warehouse": args.rejected_warehouse or "", - "rejected_serial_no": args.rejected_serial_no or "", - "asset_location": args.location or "", - "allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0, - "apply_tds": 1 if (args.total_tds or args.partial_tds) else 0 - }, - { - "item_code": args.item or args.item_code or "_Test Item", - "warehouse": args.warehouse or "_Test Warehouse - _TC", - "qty": args.qty or 5, - "received_qty": args.received_qty or 0, - "rejected_qty": args.rejected_qty or 0, - "rate": args.rate or 5000, - "price_list_rate": args.price_list_rate or 5000, - "expense_account": args.expense_account or "_Test Account Cost for Goods Sold - _TC", - "discount_account": args.discount_account or None, - "discount_amount": args.discount_amount or 0, - "conversion_factor": 1.0, - "serial_no": args.serial_no, - "stock_uom": args.uom or "_Test UOM", - "cost_center": args.cost_center or "_Test Cost Center - _TC", - "project": args.project, - "rejected_warehouse": args.rejected_warehouse or "", - "rejected_serial_no": args.rejected_serial_no or "", - "asset_location": args.location or "", - "allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0, - "apply_tds": 1 if (args.total_tds) else 0 - }, - ] - ) - - pi.save() - pi.submit() - return pi def make_purchase_invoice_against_cost_center(**args): pi = frappe.new_doc("Purchase Invoice") 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 737338e04f..06e1c17854 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -275,6 +275,11 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice" + field = ( + "base_tax_withholding_net_total as base_net_total" + if party_type == "Supplier" + else "base_net_total" + ) voucher_wise_amount = {} vouchers = [] @@ -291,7 +296,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): {"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")} ) - invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", "base_net_total"]) + invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", field]) for d in invoices_details: vouchers.append(d.name) @@ -431,11 +436,11 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers): ): # Get net total again as TDS is calculated on net total # Grand is used to just check for threshold breach - net_total = 0 - if vouchers: - net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)") - - net_total += inv.net_total + net_total = ( + frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)") + or 0.0 + ) + net_total += inv.tax_withholding_net_total supp_credit_amt = net_total - cumulative_threshold if ldc and is_valid_certificate( @@ -559,4 +564,4 @@ def is_valid_certificate( ) and certificate_limit > deducted_amount: valid = True - return valid + return valid \ No newline at end of file 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 e80fe11ab3..d29af920de 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 @@ -186,6 +186,46 @@ class TestTaxWithholdingCategory(unittest.TestCase): for d in reversed(invoices): d.cancel() + def test_tds_calculation_on_net_total_partial_tds(self): + frappe.db.set_value( + "Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS" + ) + invoices = [] + + pi = create_purchase_invoice(supplier="Test TDS Supplier4", rate=20000, do_not_save=True) + pi.extend( + "items", + [ + { + "doctype": "Purchase Invoice Item", + "item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"), + "qty": 1, + "rate": 20000, + "cost_center": "Main - _TC", + "expense_account": "Stock Received But Not Billed - _TC", + "apply_tds": 0, + }, + { + "doctype": "Purchase Invoice Item", + "item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"), + "qty": 1, + "rate": 35000, + "cost_center": "Main - _TC", + "expense_account": "Stock Received But Not Billed - _TC", + "apply_tds": 1, + }, + ], + ) + pi.save() + pi.submit() + invoices.append(pi) + + self.assertEqual(pi.taxes[0].tax_amount, 5500) + + # cancel invoices to avoid clashing + for d in reversed(invoices): + d.cancel() + def test_multi_category_single_supplier(self): frappe.db.set_value( "Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category" @@ -559,4 +599,4 @@ def create_tax_with_holding_category(): ], "accounts": [{"company": "_Test Company", "account": "TDS - _TC"}], } - ).insert() + ).insert() \ No newline at end of file diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index a0bc6ba629..16bc01dc87 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -67,13 +67,15 @@ class calculate_taxes_and_totals(object): def calculate_tax_withholding_net_total(self): if hasattr(self.doc, "tax_withholding_net_total"): - sum_net_amount = 0 + sum_base_net_amount = 0 for item in self.doc.get("items"): if hasattr(item, "apply_tds") and item.apply_tds: sum_net_amount += item.net_amount - + sum_base_net_amount += item.base_net_amount + self.doc.tax_withholding_net_total = sum_net_amount + self.doc.base_tax_withholding_net_total = sum_base_net_amount def validate_item_tax_template(self): for item in self.doc.get("items"): @@ -1076,4 +1078,4 @@ class init_landed_taxes_and_totals(object): def set_amounts_in_company_currency(self): 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")) + d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount")) \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 6a8c21f654..2624181c19 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -317,3 +317,4 @@ erpnext.patches.v14_0.fix_subcontracting_receipt_gl_entries erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger erpnext.patches.v13_0.update_schedule_type_in_loans erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization +erpnext.patches.v14_0.update_tds_fields diff --git a/erpnext/patches/v14_0/update_tds_fields.py b/erpnext/patches/v14_0/update_tds_fields.py new file mode 100644 index 0000000000..ffada07495 --- /dev/null +++ b/erpnext/patches/v14_0/update_tds_fields.py @@ -0,0 +1,25 @@ +import frappe + +from erpnext.accounts.utils import get_fiscal_year + + +def execute(): + # Only do for current fiscal year, no need to repost for all years + for company in frappe.get_all("Company"): + fiscal_year_details = get_fiscal_year(company=company.name, as_dict=True) + + purchase_invoice = frappe.qb.DocType("Purchase Invoice") + + frappe.qb.update(purchase_invoice).set( + purchase_invoice.tax_withholding_net_total, purchase_invoice.net_total + ).set( + purchase_invoice.base_tax_withholding_net_total, purchase_invoice.base_net_total + ).where( + purchase_invoice.company == company.name + ).where( + purchase_invoice.apply_tds == 1 + ).where( + purchase_invoice.posting_date >= fiscal_year_details.year_start_date + ).where( + purchase_invoice.docstatus == 1 + ).run() \ No newline at end of file diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index dd957c72ac..c2e34a6422 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1200,7 +1200,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe "base_rounding_adjustment"], company_currency); this.frm.set_currency_labels(["total", "net_total", "total_taxes_and_charges", "discount_amount", - "grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted", + "grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted","tax_withholding_net_total", "rounded_total", "in_words", "paid_amount", "write_off_amount", "operating_cost", "scrap_material_cost", "rounding_adjustment", "raw_material_cost", "total_cost"], this.frm.doc.currency); @@ -1217,7 +1217,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } // toggle fields - this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total", + this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total", "base_tax_withholding_net_total", "base_total_taxes_and_charges", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", "base_grand_total", "base_rounded_total", "base_in_words", "base_discount_amount", "base_paid_amount", "base_write_off_amount", "base_operating_cost", "base_raw_material_cost", From e1f9ba78e5769164319745300efa45ea95cd08db Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 31 Oct 2022 22:34:40 +0530 Subject: [PATCH 0449/1047] fix: Scan Barcode UX --- .../pos_invoice_item/pos_invoice_item.json | 14 +- .../sales_invoice_item.json | 11 +- erpnext/public/js/utils/barcode_scanner.js | 242 +++++++++++++++--- .../delivery_note_item.json | 11 +- .../purchase_receipt_item.json | 11 +- .../stock_entry_detail.json | 27 +- .../stock_reconciliation_item.json | 10 +- 7 files changed, 282 insertions(+), 44 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json index 3f85668ede..4bb18655b4 100644 --- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json +++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json @@ -8,6 +8,7 @@ "engine": "InnoDB", "field_order": [ "barcode", + "has_item_scanned", "item_code", "col_break1", "item_name", @@ -808,11 +809,19 @@ "fieldtype": "Check", "label": "Grant Commission", "read_only": 1 + }, + { + "default": "0", + "depends_on": "barcode", + "fieldname": "has_item_scanned", + "fieldtype": "Check", + "label": "Has Item Scanned", + "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2021-10-05 12:23:47.506290", + "modified": "2022-11-02 12:52:39.125295", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice Item", @@ -820,5 +829,6 @@ "owner": "Administrator", "permissions": [], "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 7f1a1eccc4..77055f9445 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -8,6 +8,7 @@ "engine": "InnoDB", "field_order": [ "barcode", + "has_item_scanned", "item_code", "col_break1", "item_name", @@ -872,12 +873,20 @@ "label": "Purchase Order Item", "print_hide": 1, "read_only": 1 + }, + { + "default": "0", + "depends_on": "barcode", + "fieldname": "has_item_scanned", + "fieldtype": "Check", + "label": "Has Item Scanned", + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2022-10-26 11:38:36.119339", + "modified": "2022-11-02 12:53:12.693217", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/public/js/utils/barcode_scanner.js b/erpnext/public/js/utils/barcode_scanner.js index 83b108b874..5d3a16114a 100644 --- a/erpnext/public/js/utils/barcode_scanner.js +++ b/erpnext/public/js/utils/barcode_scanner.js @@ -47,42 +47,49 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { return; } - frappe - .call({ - method: this.scan_api, - args: { - search_value: input, - }, - }) - .then((r) => { - const data = r && r.message; - if (!data || Object.keys(data).length === 0) { - this.show_alert(__("Cannot find Item with this Barcode"), "red"); - this.clean_up(); - this.play_fail_sound(); - reject(); - return; - } + this.scan_api_call(input, (r) => { + const data = r && r.message; + if (!data || Object.keys(data).length === 0) { + this.show_alert(__("Cannot find Item with this Barcode"), "red"); + this.clean_up(); + this.play_fail_sound(); + reject(); + return; + } - me.update_table(data).then(row => { - this.play_success_sound(); - resolve(row); - }).catch(() => { - this.play_fail_sound(); - reject(); - }); + me.update_table(data).then(row => { + this.play_success_sound(); + resolve(row); + }).catch(() => { + this.play_fail_sound(); + reject(); }); + }); }); } + scan_api_call(input, callback) { + frappe + .call({ + method: this.scan_api, + args: { + search_value: input, + }, + }) + .then((r) => { + callback(r); + }); + } + update_table(data) { return new Promise((resolve, reject) => { let cur_grid = this.frm.fields_dict[this.items_table_name].grid; const {item_code, barcode, batch_no, serial_no, uom} = data; - let row = this.get_row_to_modify_on_scan(item_code, batch_no, uom); + let row = this.get_row_to_modify_on_scan(item_code, batch_no, uom, barcode); + this.is_new_row = false; if (!row) { if (this.dont_allow_new_row) { this.show_alert(__("Maximum quantity scanned for item {0}.", [item_code]), "red"); @@ -90,6 +97,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { reject(); return; } + this.is_new_row = true; // add new row if new item/batch is scanned row = frappe.model.add_child(this.frm.doc, cur_grid.doctype, this.items_table_name); @@ -105,7 +113,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { frappe.run_serially([ () => this.set_selector_trigger_flag(data), - () => this.set_item(row, item_code).then(qty => { + () => this.set_item(row, item_code, barcode, batch_no, serial_no).then(qty => { this.show_scan_message(row.idx, row.item_code, qty); }), () => this.set_barcode_uom(row, uom), @@ -136,7 +144,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { frappe.flags.hide_serial_batch_dialog = false; } - set_item(row, item_code) { + set_item(row, item_code, barcode, batch_no, serial_no) { return new Promise(resolve => { const increment = async (value = 1) => { const item_data = {item_code: item_code}; @@ -150,11 +158,184 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { increment(value).then((value) => resolve(value)); }); } else { - increment().then((value) => resolve(value)); + this.prepare_item_for_scan(row, item_code, barcode, batch_no, serial_no); } }); } + prepare_item_for_scan(row, item_code, barcode, batch_no, serial_no) { + var me = this; + this.dialog = new frappe.ui.Dialog({ + title: __("Scan barcode for item {0}", [item_code]), + fields: me.get_fields_for_dialog(row, item_code, barcode, batch_no, serial_no), + }) + + this.dialog.set_primary_action(__("Update"), () => { + const item_data = {item_code: item_code}; + item_data[this.qty_field] = this.dialog.get_value("scanned_qty"); + item_data["has_item_scanned"] = 1; + + this.remaining_qty = flt(this.dialog.get_value("qty")) - flt(this.dialog.get_value("scanned_qty")); + frappe.model.set_value(row.doctype, row.name, item_data); + + frappe.run_serially([ + () => this.set_batch_no(row, this.dialog.get_value("batch_no")), + () => this.set_barcode(row, this.dialog.get_value("barcode")), + () => this.set_serial_no(row, this.dialog.get_value("serial_no")), + () => this.add_child_for_remaining_qty(row), + () => this.clean_up() + ]); + + this.dialog.hide(); + }); + + this.dialog.show(); + + this.$scan_btn = this.dialog.$wrapper.find(".link-btn"); + this.$scan_btn.css("display", "inline"); + } + + get_fields_for_dialog(row, item_code, barcode, batch_no, serial_no) { + let fields = [ + { + fieldtype: "Data", + fieldname: "barcode_scanner", + options: "Barcode", + label: __("Scan Barcode"), + onchange: (e) => { + if (!e) { + return; + } + + debugger + if (e.target.value) { + this.scan_api_call(e.target.value, (r) => { + if (r.message) { + this.update_dialog_values(item_code, r); + } + }) + } + } + }, + { + fieldtype: "Section Break", + }, + { + fieldtype: "Float", + fieldname: "qty", + label: __("Quantity to Scan"), + default: row[this.qty_field] || 1, + }, + { + fieldtype: "Column Break", + fieldname: "column_break_1", + }, + { + fieldtype: "Float", + read_only: 1, + fieldname: "scanned_qty", + label: __("Scanned Quantity"), + default: 1, + }, + { + fieldtype: "Section Break", + } + ] + + if (batch_no) { + fields.push({ + fieldtype: "Link", + fieldname: "batch_no", + options: "Batch No", + label: __("Batch No"), + default: batch_no, + read_only: 1, + hidden: 1 + }); + } + + if (serial_no) { + fields.push({ + fieldtype: "Small Text", + fieldname: "serial_no", + label: __("Serial Nos"), + default: serial_no, + read_only: 1, + }); + } + + if (barcode) { + fields.push({ + fieldtype: "Data", + fieldname: "barcode", + options: "Barcode", + label: __("Barcode"), + default: barcode, + read_only: 1, + hidden: 1 + }); + } + + return fields; + } + + update_dialog_values(scanned_item, r) { + const {item_code, barcode, batch_no, serial_no, uom} = r.message; + + this.dialog.set_value("barcode_scanner", ""); + if (item_code === scanned_item && + (this.dialog.get_value("barcode") === barcode || batch_no || serial_no)) { + + if (batch_no) { + this.dialog.set_value("batch_no", batch_no); + } + + if (serial_no) { + + this.validate_duplicate_serial_no(serial_no); + let serial_nos = this.dialog.get_value("serial_no") + "\n" + serial_no; + this.dialog.set_value("serial_no", serial_nos); + } + + let qty = flt(this.dialog.get_value("scanned_qty")) + 1.0; + this.dialog.set_value("scanned_qty", qty); + } + } + + validate_duplicate_serial_no(serial_no) { + let serial_nos = this.dialog.get_value("serial_no") ? + this.dialog.get_value("serial_no").split("\n") : []; + + if (in_list(serial_nos, serial_no)) { + frappe.throw(__("Serial No {0} already scanned", [serial_no])); + } + } + + add_child_for_remaining_qty(prev_row) { + if (this.remaining_qty && this.remaining_qty >0) { + let cur_grid = this.frm.fields_dict[this.items_table_name].grid; + let row = frappe.model.add_child(this.frm.doc, cur_grid.doctype, this.items_table_name); + + let ignore_fields = ["name", "idx", "batch_no", "barcode", + "received_qty", "serial_no", "has_item_scanned"]; + + for (let key in prev_row) { + if (in_list(ignore_fields, key)) { + continue; + } + + row[key] = prev_row[key]; + } + + row[this.qty_field] = this.remaining_qty; + if (this.qty_field == "qty" && frappe.meta.has_field(row.doctype, "stock_qty")) { + row["stock_qty"] = this.remaining_qty * row.conversion_factor; + } + + this.frm.script_manager.trigger("item_code", row.doctype, row.name); + } + } + async set_serial_no(row, serial_no) { if (serial_no && frappe.meta.has_field(row.doctype, this.serial_no_field)) { const existing_serial_nos = row[this.serial_no_field]; @@ -176,6 +357,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { } async set_batch_no(row, batch_no) { + debugger if (batch_no && frappe.meta.has_field(row.doctype, this.batch_no_field)) { await frappe.model.set_value(row.doctype, row.name, this.batch_no_field, batch_no); } @@ -205,7 +387,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { return is_duplicate; } - get_row_to_modify_on_scan(item_code, batch_no, uom) { + get_row_to_modify_on_scan(item_code, batch_no, uom, barcode) { let cur_grid = this.frm.fields_dict[this.items_table_name].grid; // Check if batch is scanned and table has batch no field @@ -214,12 +396,14 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { const matching_row = (row) => { const item_match = row.item_code == item_code; - const batch_match = row[this.batch_no_field] == batch_no; + const batch_match = (!row[this.batch_no_field] || row[this.batch_no_field] == batch_no); const uom_match = !uom || row[this.uom_field] == uom; const qty_in_limit = flt(row[this.qty_field]) < flt(row[this.max_qty_field]); + const item_scanned = row.has_item_scanned; return item_match && uom_match + && !item_scanned && (!is_batch_no_scan || batch_match) && (!check_max_qty || qty_in_limit) } diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 77c3253432..3229463070 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -8,6 +8,7 @@ "engine": "InnoDB", "field_order": [ "barcode", + "has_item_scanned", "item_code", "item_name", "col_break1", @@ -809,13 +810,21 @@ "label": "Purchase Order Item", "print_hide": 1, "read_only": 1 + }, + { + "default": "0", + "depends_on": "barcode", + "fieldname": "has_item_scanned", + "fieldtype": "Check", + "label": "Has Item Scanned", + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-10-26 16:05:17.720768", + "modified": "2022-11-02 12:54:07.225623", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 474ee92e26..557bb594bf 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -8,6 +8,7 @@ "engine": "InnoDB", "field_order": [ "barcode", + "has_item_scanned", "section_break_2", "item_code", "product_bundle", @@ -996,12 +997,20 @@ { "fieldname": "column_break_102", "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "barcode", + "fieldname": "has_item_scanned", + "fieldtype": "Check", + "label": "Has Item Scanned", + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2022-10-26 16:06:02.524435", + "modified": "2022-11-02 12:49:28.746701", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 5fe11a2aa7..95f4f5fd36 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -8,6 +8,7 @@ "engine": "InnoDB", "field_order": [ "barcode", + "has_item_scanned", "section_break_2", "s_warehouse", "col_break1", @@ -498,14 +499,14 @@ "read_only": 1 }, { - "fieldname": "sco_rm_detail", - "fieldtype": "Data", - "hidden": 1, - "label": "SCO Supplied Item", - "no_copy": 1, - "print_hide": 1, - "read_only": 1 - }, + "fieldname": "sco_rm_detail", + "fieldtype": "Data", + "hidden": 1, + "label": "SCO Supplied Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { "default": "0", "depends_on": "eval:parent.purpose===\"Repack\" && doc.t_warehouse", @@ -563,13 +564,21 @@ "fieldname": "is_process_loss", "fieldtype": "Check", "label": "Is Process Loss" + }, + { + "default": "0", + "depends_on": "barcode", + "fieldname": "has_item_scanned", + "fieldtype": "Check", + "label": "Has Item Scanned", + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-06-17 05:06:33.621264", + "modified": "2022-11-02 13:00:34.258828", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json index 79c2fcc252..7c3e151663 100644 --- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json +++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json @@ -7,6 +7,7 @@ "engine": "InnoDB", "field_order": [ "barcode", + "has_item_scanned", "item_code", "item_name", "warehouse", @@ -177,11 +178,18 @@ "label": "Allow Zero Valuation Rate", "print_hide": 1, "read_only": 1 + }, + { + "depends_on": "barcode", + "fieldname": "has_item_scanned", + "fieldtype": "Data", + "label": "Has Item Scanned", + "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2022-04-02 04:19:40.380587", + "modified": "2022-11-02 13:01:23.580937", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reconciliation Item", From 81d791eea0eaec86d532a0a248752976eeaf08c2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 2 Nov 2022 15:40:56 +0530 Subject: [PATCH 0450/1047] test: refactor use @change_settings decorator when possible --- .../doctype/purchase_order/test_purchase_order.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 6c1bcc7dd4..e0981c697b 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -5,7 +5,7 @@ import json import frappe -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, flt, getdate, nowdate from frappe.utils.data import today @@ -709,13 +709,10 @@ class TestPurchaseOrder(FrappeTestCase): pi.insert() self.assertTrue(pi.get("payment_schedule")) + @change_settings("Accounts Settings", {"unlink_advance_payment_on_cancelation_of_order": 1}) def test_advance_payment_entry_unlink_against_purchase_order(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry - frappe.db.set_value( - "Accounts Settings", "Accounts Settings", "unlink_advance_payment_on_cancelation_of_order", 1 - ) - po_doc = create_purchase_order() pe = get_payment_entry("Purchase Order", po_doc.name, bank_account="_Test Bank - _TC") @@ -735,10 +732,6 @@ class TestPurchaseOrder(FrappeTestCase): pe_doc = frappe.get_doc("Payment Entry", pe.name) pe_doc.cancel() - frappe.db.set_value( - "Accounts Settings", "Accounts Settings", "unlink_advance_payment_on_cancelation_of_order", 0 - ) - def test_schedule_date(self): po = create_purchase_order(do_not_submit=True) po.schedule_date = None From 1a0a8ac7e238ab3ca912b0912e76b1de69ace363 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 2 Nov 2022 15:43:45 +0530 Subject: [PATCH 0451/1047] test: PO advance paid on payment submission and cancellation --- .../purchase_order/test_purchase_order.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index e0981c697b..5a96131157 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -732,6 +732,32 @@ class TestPurchaseOrder(FrappeTestCase): pe_doc = frappe.get_doc("Payment Entry", pe.name) pe_doc.cancel() + @change_settings("Accounts Settings", {"unlink_advance_payment_on_cancelation_of_order": 1}) + def test_advance_paid_upon_payment_entry_cancellation(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry + + po_doc = create_purchase_order() + + pe = get_payment_entry("Purchase Order", po_doc.name, bank_account="_Test Bank - _TC") + pe.reference_no = "1" + pe.reference_date = nowdate() + pe.paid_from_account_currency = po_doc.currency + pe.paid_to_account_currency = po_doc.currency + pe.source_exchange_rate = 1 + pe.target_exchange_rate = 1 + pe.paid_amount = po_doc.grand_total + pe.save(ignore_permissions=True) + pe.submit() + + po_doc.reload() + self.assertEqual(po_doc.advance_paid, po_doc.base_grand_total) + + pe_doc = frappe.get_doc("Payment Entry", pe.name) + pe_doc.cancel() + + po_doc.reload() + self.assertEqual(po_doc.advance_paid, 0) + def test_schedule_date(self): po = create_purchase_order(do_not_submit=True) po.schedule_date = None From 721ac6b847ed56943621697123359d7d37360a3e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 2 Nov 2022 15:54:23 +0530 Subject: [PATCH 0452/1047] test: SO advance paid on Payment submission and cancellation --- .../doctype/sales_order/test_sales_order.py | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index adfb39c1ae..dfa341b1fc 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -6,7 +6,7 @@ import json import frappe import frappe.permissions from frappe.core.doctype.user_permission.test_user_permission import create_user -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, flt, getdate, nowdate, today from erpnext.controllers.accounts_controller import update_child_qty_rate @@ -1346,6 +1346,33 @@ class TestSalesOrder(FrappeTestCase): self.assertRaises(frappe.LinkExistsError, so_doc.cancel) + @change_settings("Accounts Settings", {"unlink_advance_payment_on_cancelation_of_order": 1}) + def test_advance_paid_upon_payment_cancellation(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry + + so = make_sales_order() + + pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Bank - _TC") + pe.reference_no = "1" + pe.reference_date = nowdate() + pe.paid_from_account_currency = so.currency + pe.paid_to_account_currency = so.currency + pe.source_exchange_rate = 1 + pe.target_exchange_rate = 1 + pe.paid_amount = so.grand_total + pe.save(ignore_permissions=True) + pe.submit() + so.reload() + + self.assertEqual(so.advance_paid, so.base_grand_total) + + # cancel advance payment + pe.reload() + pe.cancel() + + so.reload() + self.assertEqual(so.advance_paid, 0) + def test_cancel_sales_order_after_cancel_payment_entry(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry From 12456f985038f6eea37fbe930fe84168f5f36c68 Mon Sep 17 00:00:00 2001 From: niralisatapara Date: Thu, 3 Nov 2022 10:46:30 +0530 Subject: [PATCH 0453/1047] feat: item wise tds calculation --- .../doctype/purchase_invoice/test_purchase_invoice.py | 2 +- .../tax_withholding_category/tax_withholding_category.py | 4 ++-- .../tax_withholding_category/test_tax_withholding_category.py | 2 +- erpnext/controllers/taxes_and_totals.py | 2 +- 4 files 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 76ea95528c..f901257ccf 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1734,4 +1734,4 @@ def make_purchase_invoice_against_cost_center(**args): return pi -test_records = frappe.get_test_records("Purchase Invoice") \ No newline at end of file +test_records = frappe.get_test_records("Purchase Invoice") 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 06e1c17854..30ed91b974 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -63,7 +63,7 @@ def get_party_details(inv): def get_party_tax_withholding_details(inv, tax_withholding_category=None): if inv.doctype == "Payment Entry": inv.tax_withholding_net_total = inv.net_total - + pan_no = "" parties = [] party_type, party = get_party_details(inv) @@ -564,4 +564,4 @@ def is_valid_certificate( ) and certificate_limit > deducted_amount: valid = True - return valid \ No newline at end of file + return valid 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 d29af920de..40c732bae5 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 @@ -599,4 +599,4 @@ def create_tax_with_holding_category(): ], "accounts": [{"company": "_Test Company", "account": "TDS - _TC"}], } - ).insert() \ No newline at end of file + ).insert() diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 16bc01dc87..81de682378 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -1078,4 +1078,4 @@ class init_landed_taxes_and_totals(object): def set_amounts_in_company_currency(self): 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")) \ No newline at end of file + d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount")) From b0fc568c80ec5bead83fc0bc61be78e95ba24813 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 3 Nov 2022 11:24:58 +0530 Subject: [PATCH 0454/1047] fix: not able to select customer / supplier --- erpnext/buying/doctype/supplier/test_supplier.py | 4 ++++ erpnext/controllers/queries.py | 4 ++-- erpnext/selling/doctype/customer/test_customer.py | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py index e2dbf21be2..b9fc344647 100644 --- a/erpnext/buying/doctype/supplier/test_supplier.py +++ b/erpnext/buying/doctype/supplier/test_supplier.py @@ -156,6 +156,8 @@ class TestSupplier(FrappeTestCase): def test_serach_fields_for_supplier(self): from erpnext.controllers.queries import supplier_query + frappe.db.set_value("Buying Settings", None, "supp_master_name", "Naming Series") + supplier_name = create_supplier(supplier_name="Test Supplier 1").name make_property_setter( @@ -187,6 +189,8 @@ class TestSupplier(FrappeTestCase): self.assertEqual(data[0].supplier_type, "Company") self.assertTrue("supplier_type" in data[0]) + frappe.db.set_value("Buying Settings", None, "supp_master_name", "Supplier Name") + def create_supplier(**args): args = frappe._dict(args) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 3bdc017068..b0cf724166 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -85,7 +85,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters, as_dict= fields = ["name"] if cust_master_name != "Customer Name": - fields = ["customer_name"] + fields.append("customer_name") fields = get_fields(doctype, fields) searchfields = frappe.get_meta(doctype).get_search_fields() @@ -123,7 +123,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters, as_dict= fields = ["name"] if supp_master_name != "Supplier Name": - fields = ["supplier_name"] + fields.append("supplier_name") fields = get_fields(doctype, fields) diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 691adccd4d..a621c737ed 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -345,6 +345,8 @@ class TestCustomer(FrappeTestCase): def test_serach_fields_for_customer(self): from erpnext.controllers.queries import customer_query + frappe.db.set_value("Selling Settings", None, "cust_master_name", "Naming Series") + make_property_setter( "Customer", None, "search_fields", "customer_group", "Data", for_doctype="Doctype" ) @@ -369,6 +371,8 @@ class TestCustomer(FrappeTestCase): self.assertEqual(data[0].territory, "_Test Territory") self.assertTrue("territory" in data[0]) + frappe.db.set_value("Selling Settings", None, "cust_master_name", "Customer Name") + def get_customer_dict(customer_name): return { From 27df455b9862396a192ce381f2e34f0d3cb94e5e Mon Sep 17 00:00:00 2001 From: Daizy Modi Date: Thu, 3 Nov 2022 13:38:48 +0530 Subject: [PATCH 0455/1047] perf: use `get_cached_value` instead of `db.get_value` in accounts module --- .../account_balance_timeline.py | 2 +- erpnext/accounts/doctype/account/account.py | 38 ++++++++++--------- .../chart_of_accounts/chart_of_accounts.py | 4 +- .../doctype/bank_account/bank_account.py | 2 +- .../bank_reconciliation_tool.py | 20 ++++++---- .../bank_transaction_upload.py | 2 +- erpnext/accounts/doctype/budget/budget.py | 6 +-- .../chart_of_accounts_importer.py | 4 +- erpnext/accounts/doctype/dunning/dunning.py | 2 +- .../exchange_rate_revaluation.py | 2 +- .../doctype/fiscal_year/fiscal_year.py | 2 +- erpnext/accounts/doctype/gl_entry/gl_entry.py | 8 ++-- .../item_tax_template/item_tax_template.py | 2 +- .../doctype/journal_entry/journal_entry.py | 18 ++++----- .../mode_of_payment/mode_of_payment.py | 2 +- .../doctype/payment_entry/payment_entry.py | 12 +++--- .../payment_gateway_account.py | 2 +- .../payment_ledger_entry.py | 2 +- .../payment_request/payment_request.py | 2 +- .../period_closing_voucher.py | 2 +- .../doctype/pos_invoice/pos_invoice.py | 7 ++-- .../purchase_invoice/purchase_invoice.py | 10 ++--- .../sales_taxes_and_charges_template.py | 2 +- erpnext/accounts/party.py | 14 ++++--- .../bank_reconciliation_statement.py | 2 +- .../accounts/report/cash_flow/cash_flow.py | 2 +- .../consolidated_financial_statement.py | 2 +- .../customer_ledger_summary.py | 4 +- .../dimension_wise_accounts_balance_report.py | 2 +- .../accounts/report/financial_statements.py | 2 +- .../report/general_ledger/general_ledger.py | 4 +- .../report/trial_balance/trial_balance.py | 4 +- erpnext/accounts/utils.py | 2 +- 33 files changed, 101 insertions(+), 90 deletions(-) diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py index fefec0ee7b..f091a4ff5a 100644 --- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py +++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py @@ -76,7 +76,7 @@ def get( def build_result(account, dates, gl_entries): result = [[getdate(date), 0.0] for date in dates] - root_type = frappe.db.get_value("Account", account, "root_type") + root_type = frappe.get_cached_value("Account", account, "root_type") # start with the first date date_index = 0 diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 9dff1168fd..8beb01b2cf 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -58,7 +58,7 @@ class Account(NestedSet): def validate_parent(self): """Fetch Parent Details and validate parent account""" if self.parent_account: - par = frappe.db.get_value( + par = frappe.get_cached_value( "Account", self.parent_account, ["name", "is_group", "company"], as_dict=1 ) if not par: @@ -82,7 +82,7 @@ class Account(NestedSet): def set_root_and_report_type(self): if self.parent_account: - par = frappe.db.get_value( + par = frappe.get_cached_value( "Account", self.parent_account, ["report_type", "root_type"], as_dict=1 ) @@ -92,7 +92,9 @@ class Account(NestedSet): self.root_type = par.root_type if self.is_group: - db_value = frappe.db.get_value("Account", self.name, ["report_type", "root_type"], as_dict=1) + db_value = frappe.get_cached_value( + "Account", self.name, ["report_type", "root_type"], as_dict=1 + ) if db_value: if self.report_type != db_value.report_type: frappe.db.sql( @@ -113,7 +115,7 @@ class Account(NestedSet): def validate_root_details(self): # does not exists parent if frappe.db.exists("Account", self.name): - if not frappe.db.get_value("Account", self.name, "parent_account"): + if not frappe.get_cached_value("Account", self.name, "parent_account"): throw(_("Root cannot be edited."), RootNotEditable) if not self.parent_account and not self.is_group: @@ -127,7 +129,9 @@ class Account(NestedSet): return ancestors = get_root_company(self.company) if ancestors: - if frappe.get_value("Company", self.company, "allow_account_creation_against_child_company"): + if frappe.get_cached_value( + "Company", self.company, "allow_account_creation_against_child_company" + ): return if not frappe.db.get_value( "Account", {"account_name": self.account_name, "company": ancestors[0]}, "name" @@ -138,7 +142,7 @@ class Account(NestedSet): if not descendants: return parent_acc_name_map = {} - parent_acc_name, parent_acc_number = frappe.db.get_value( + parent_acc_name, parent_acc_number = frappe.get_cached_value( "Account", self.parent_account, ["account_name", "account_number"] ) filters = { @@ -162,7 +166,7 @@ class Account(NestedSet): if self.get("__islocal"): return - existing_is_group = frappe.db.get_value("Account", self.name, "is_group") + existing_is_group = frappe.get_cached_value("Account", self.name, "is_group") if cint(self.is_group) != cint(existing_is_group): if self.check_gle_exists(): throw(_("Account with existing transaction cannot be converted to ledger")) @@ -173,7 +177,7 @@ class Account(NestedSet): throw(_("Account with child nodes cannot be set as ledger")) def validate_frozen_accounts_modifier(self): - old_value = frappe.db.get_value("Account", self.name, "freeze_account") + old_value = frappe.get_cached_value("Account", self.name, "freeze_account") if old_value and old_value != self.freeze_account: frozen_accounts_modifier = frappe.db.get_value( "Accounts Settings", None, "frozen_accounts_modifier" @@ -223,9 +227,9 @@ class Account(NestedSet): ) # validate if parent of child company account to be added is a group - if frappe.db.get_value("Account", self.parent_account, "is_group") and not frappe.db.get_value( - "Account", parent_acc_name_map[company], "is_group" - ): + if frappe.get_cached_value( + "Account", self.parent_account, "is_group" + ) and not frappe.get_cached_value("Account", parent_acc_name_map[company], "is_group"): msg = _( "While creating account for Child Company {0}, parent account {1} found as a ledger account." ).format(company_bold, parent_acc_name_bold) @@ -377,17 +381,17 @@ def validate_account_number(name, account_number, company): @frappe.whitelist() def update_account_number(name, account_name, account_number=None, from_descendant=False): - account = frappe.db.get_value("Account", name, "company", as_dict=True) + account = frappe.get_cached_value("Account", name, "company", as_dict=True) if not account: return - old_acc_name, old_acc_number = frappe.db.get_value( + old_acc_name, old_acc_number = frappe.get_cached_value( "Account", name, ["account_name", "account_number"] ) # check if account exists in parent company ancestors = get_ancestors_of("Company", account.company) - allow_independent_account_creation = frappe.get_value( + allow_independent_account_creation = frappe.get_cached_value( "Company", account.company, "allow_account_creation_against_child_company" ) @@ -438,7 +442,7 @@ def merge_account(old, new, is_group, root_type, company): if not frappe.db.exists("Account", new): throw(_("Account {0} does not exist").format(new)) - val = list(frappe.db.get_value("Account", new, ["is_group", "root_type", "company"])) + val = list(frappe.get_cached_value("Account", new, ["is_group", "root_type", "company"])) if val != [cint(is_group), root_type, company]: throw( @@ -447,9 +451,9 @@ def merge_account(old, new, is_group, root_type, company): ) ) - if is_group and frappe.db.get_value("Account", new, "parent_account") == old: + if is_group and frappe.get_cached_value("Account", new, "parent_account") == old: frappe.db.set_value( - "Account", new, "parent_account", frappe.db.get_value("Account", old, "parent_account") + "Account", new, "parent_account", frappe.get_cached_value("Account", old, "parent_account") ) frappe.rename_doc("Account", old, new, merge=1, force=1) diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py index 947b4853e8..75f8f0645c 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py +++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py @@ -53,7 +53,7 @@ def create_charts( "account_number": account_number, "account_type": child.get("account_type"), "account_currency": child.get("account_currency") - or frappe.db.get_value("Company", company, "default_currency"), + or frappe.get_cached_value("Company", company, "default_currency"), "tax_rate": child.get("tax_rate"), } ) @@ -148,7 +148,7 @@ def get_charts_for_country(country, with_standard=False): ) or frappe.local.flags.allow_unverified_charts: charts.append(content["name"]) - country_code = frappe.db.get_value("Country", country, "code") + country_code = frappe.get_cached_value("Country", country, "code") if country_code: folders = ("verified",) if frappe.local.flags.allow_unverified_charts: diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py index addcf62e5b..b91f0f9137 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.py +++ b/erpnext/accounts/doctype/bank_account/bank_account.py @@ -77,6 +77,6 @@ def get_party_bank_account(party_type, party): @frappe.whitelist() def get_bank_account_details(bank_account): - return frappe.db.get_value( + return frappe.get_cached_value( "Bank Account", bank_account, ["account", "bank", "bank_account_no"], as_dict=1 ) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py index cc3727ce83..d353270b45 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -57,7 +57,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None): @frappe.whitelist() def get_account_balance(bank_account, till_date): # returns account balance till the specified date - account = frappe.db.get_value("Bank Account", bank_account, "account") + account = frappe.get_cached_value("Bank Account", bank_account, "account") filters = frappe._dict( {"account": account, "report_date": till_date, "include_pos_transactions": 1} ) @@ -130,8 +130,10 @@ def create_journal_entry_bts( fieldname=["name", "deposit", "withdrawal", "bank_account"], as_dict=True, )[0] - company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account") - account_type = frappe.db.get_value("Account", second_account, "account_type") + company_account = frappe.get_cached_value( + "Bank Account", bank_transaction.bank_account, "account" + ) + account_type = frappe.get_cached_value("Account", second_account, "account_type") if account_type in ["Receivable", "Payable"]: if not (party_type and party): frappe.throw( @@ -164,7 +166,7 @@ def create_journal_entry_bts( } ) - company = frappe.get_value("Account", company_account, "company") + company = frappe.get_cached_value("Account", company_account, "company") journal_entry_dict = { "voucher_type": entry_type, @@ -219,8 +221,10 @@ def create_payment_entry_bts( paid_amount = bank_transaction.unallocated_amount payment_type = "Receive" if bank_transaction.deposit > 0 else "Pay" - company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account") - company = frappe.get_value("Account", company_account, "company") + company_account = frappe.get_cached_value( + "Bank Account", bank_transaction.bank_account, "account" + ) + company = frappe.get_cached_value("Account", company_account, "company") payment_entry_dict = { "company": company, "payment_type": payment_type, @@ -266,7 +270,7 @@ def reconcile_vouchers(bank_transaction_name, vouchers): # updated clear date of all the vouchers based on the bank transaction vouchers = json.loads(vouchers) transaction = frappe.get_doc("Bank Transaction", bank_transaction_name) - company_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account") + company_account = frappe.get_cached_value("Bank Account", transaction.bank_account, "account") if transaction.unallocated_amount == 0: frappe.throw(_("This bank transaction is already fully reconciled")) @@ -290,7 +294,7 @@ def reconcile_vouchers(bank_transaction_name, vouchers): "The sum total of amounts of all selected vouchers should be less than the unallocated amount of the bank transaction" ) ) - account = frappe.db.get_value("Bank Account", transaction.bank_account, "account") + account = frappe.get_cached_value("Bank Account", transaction.bank_account, "account") for voucher in vouchers: gl_entry = frappe.db.get_value( diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py index 372c53d499..efb9d8c5ba 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py @@ -74,7 +74,7 @@ def get_header_mapping(columns, bank_account): def get_bank_mapping(bank_account): - bank_name = frappe.db.get_value("Bank Account", bank_account, "bank") + bank_name = frappe.get_cached_value("Bank Account", bank_account, "bank") bank = frappe.get_doc("Bank", bank_name) mapping = {row.file_field: row.bank_transaction_field for row in bank.bank_transaction_mapping} diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py index 637ac7a04c..53838fbd17 100644 --- a/erpnext/accounts/doctype/budget/budget.py +++ b/erpnext/accounts/doctype/budget/budget.py @@ -59,7 +59,7 @@ class Budget(Document): account_list = [] for d in self.get("accounts"): if d.account: - account_details = frappe.db.get_value( + account_details = frappe.get_cached_value( "Account", d.account, ["is_group", "company", "report_type"], as_dict=1 ) @@ -306,7 +306,7 @@ def get_other_condition(args, budget, for_doc): if args.get("fiscal_year"): date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date" - start_date, end_date = frappe.db.get_value( + start_date, end_date = frappe.get_cached_value( "Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"] ) @@ -379,7 +379,7 @@ def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_ye ): distribution.setdefault(d.month, d.percentage_allocation) - dt = frappe.db.get_value("Fiscal Year", fiscal_year, "year_start_date") + dt = frappe.get_cached_value("Fiscal Year", fiscal_year, "year_start_date") accumulated_percentage = 0.0 while dt <= getdate(posting_date): diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index 01bf1c23e9..30913949a2 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -45,8 +45,8 @@ def validate_columns(data): @frappe.whitelist() def validate_company(company): - parent_company, allow_account_creation_against_child_company = frappe.db.get_value( - "Company", {"name": company}, ["parent_company", "allow_account_creation_against_child_company"] + parent_company, allow_account_creation_against_child_company = frappe.get_cached_value( + "Company", company, ["parent_company", "allow_account_creation_against_child_company"] ) if parent_company and (not allow_account_creation_against_child_company): diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py index 9874d66fa5..331adb4b8e 100644 --- a/erpnext/accounts/doctype/dunning/dunning.py +++ b/erpnext/accounts/doctype/dunning/dunning.py @@ -19,7 +19,7 @@ class Dunning(AccountsController): self.validate_overdue_days() self.validate_amount() if not self.income_account: - self.income_account = frappe.db.get_value("Company", self.company, "default_income_account") + self.income_account = frappe.get_cached_value("Company", self.company, "default_income_account") def validate_overdue_days(self): self.overdue_days = (getdate(self.posting_date) - getdate(self.due_date)).days or 0 diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py index 2f81c5fb75..03d0e49902 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py @@ -222,7 +222,7 @@ class ExchangeRateRevaluation(Document): @frappe.whitelist() def get_account_details(account, company, posting_date, party_type=None, party=None): - account_currency, account_type = frappe.db.get_value( + account_currency, account_type = frappe.get_cached_value( "Account", account, ["account_currency", "account_type"] ) if account_type in ["Receivable", "Payable"] and not (party_type and party): diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py index 069ab5ea84..4592421304 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py @@ -170,4 +170,4 @@ def auto_create_fiscal_year(): def get_from_and_to_date(fiscal_year): fields = ["year_start_date as from_date", "year_end_date as to_date"] - return frappe.db.get_value("Fiscal Year", fiscal_year, fields, as_dict=1) + return frappe.get_cached_value("Fiscal Year", fiscal_year, fields, as_dict=1) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 7227b95818..f312048207 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -58,7 +58,7 @@ class GLEntry(Document): validate_balance_type(self.account, adv_adj) validate_frozen_account(self.account, adv_adj) - if frappe.db.get_value("Account", self.account, "account_type") not in [ + if frappe.get_cached_value("Account", self.account, "account_type") not in [ "Receivable", "Payable", ]: @@ -120,7 +120,7 @@ class GLEntry(Document): frappe.throw(msg, title=_("Missing Cost Center")) def validate_dimensions_for_pl_and_bs(self): - account_type = frappe.db.get_value("Account", self.account, "report_type") + account_type = frappe.get_cached_value("Account", self.account, "report_type") for dimension in get_checks_for_pl_and_bs_accounts(): if ( @@ -188,7 +188,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 frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss" and not self.is_cancelled ): frappe.throw( @@ -281,7 +281,7 @@ class GLEntry(Document): def validate_balance_type(account, adv_adj=False): if not adv_adj and account: - balance_must_be = frappe.db.get_value("Account", account, "balance_must_be") + balance_must_be = frappe.get_cached_value("Account", account, "balance_must_be") if balance_must_be: balance = frappe.db.sql( """select sum(debit) - sum(credit) diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.py b/erpnext/accounts/doctype/item_tax_template/item_tax_template.py index 23f36ec6d8..7e2fca8d24 100644 --- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.py +++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.py @@ -21,7 +21,7 @@ class ItemTaxTemplate(Document): check_list = [] for d in self.get("taxes"): if d.tax_type: - account_type = frappe.db.get_value("Account", d.tax_type, "account_type") + account_type = frappe.get_cached_value("Account", d.tax_type, "account_type") if account_type not in [ "Tax", diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index de012b28ec..1714fffc16 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -319,7 +319,7 @@ class JournalEntry(AccountsController): def validate_party(self): for d in self.get("accounts"): - account_type = frappe.db.get_value("Account", d.account, "account_type") + account_type = frappe.get_cached_value("Account", d.account, "account_type") if account_type in ["Receivable", "Payable"]: if not (d.party_type and d.party): frappe.throw( @@ -382,7 +382,7 @@ class JournalEntry(AccountsController): def validate_against_jv(self): for d in self.get("accounts"): if d.reference_type == "Journal Entry": - account_root_type = frappe.db.get_value("Account", d.account, "root_type") + account_root_type = frappe.get_cached_value("Account", d.account, "root_type") if account_root_type == "Asset" and flt(d.debit) > 0: frappe.throw( _( @@ -631,7 +631,7 @@ class JournalEntry(AccountsController): def validate_multi_currency(self): alternate_currency = [] for d in self.get("accounts"): - account = frappe.db.get_value( + account = frappe.get_cached_value( "Account", d.account, ["account_currency", "account_type"], as_dict=1 ) if account: @@ -762,7 +762,7 @@ class JournalEntry(AccountsController): party_amount += d.debit_in_account_currency or d.credit_in_account_currency party_account_currency = d.account_currency - elif frappe.db.get_value("Account", d.account, "account_type") in ["Bank", "Cash"]: + elif frappe.get_cached_value("Account", d.account, "account_type") in ["Bank", "Cash"]: bank_amount += d.debit_in_account_currency or d.credit_in_account_currency bank_account_currency = d.account_currency @@ -987,7 +987,7 @@ def get_default_bank_cash_account(company, account_type=None, mode_of_payment=No account = account_list[0].name if account: - account_details = frappe.db.get_value( + account_details = frappe.get_cached_value( "Account", account, ["account_currency", "account_type"], as_dict=1 ) @@ -1116,7 +1116,7 @@ def get_payment_entry(ref_doc, args): "party_type": args.get("party_type"), "party": ref_doc.get(args.get("party_type").lower()), "cost_center": cost_center, - "account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"), + "account_type": frappe.get_cached_value("Account", args.get("party_account"), "account_type"), "account_currency": args.get("party_account_currency") or get_account_currency(args.get("party_account")), "balance": get_balance_on(args.get("party_account")), @@ -1283,7 +1283,7 @@ def get_party_account_and_balance(company, party_type, party, cost_center=None): "account": account, "balance": account_balance, "party_balance": party_balance, - "account_currency": frappe.db.get_value("Account", account, "account_currency"), + "account_currency": frappe.get_cached_value("Account", account, "account_currency"), } @@ -1296,7 +1296,7 @@ def get_account_balance_and_party_type( frappe.msgprint(_("No Permission"), raise_exception=1) company_currency = erpnext.get_company_currency(company) - account_details = frappe.db.get_value( + account_details = frappe.get_cached_value( "Account", account, ["account_type", "account_currency"], as_dict=1 ) @@ -1349,7 +1349,7 @@ def get_exchange_rate( ): from erpnext.setup.utils import get_exchange_rate - account_details = frappe.db.get_value( + account_details = frappe.get_cached_value( "Account", account, ["account_type", "root_type", "account_currency", "company"], as_dict=1 ) diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py index ed35d1e094..7d6ef3c3bf 100644 --- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py +++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py @@ -25,7 +25,7 @@ class ModeofPayment(Document): def validate_accounts(self): for entry in self.accounts: """Error when Company of Ledger account doesn't match with Company Selected""" - if frappe.db.get_value("Account", entry.default_account, "company") != entry.company: + if frappe.get_cached_value("Account", entry.default_account, "company") != entry.company: frappe.throw( _("Account {0} does not match with Company {1} in Mode of Account: {2}").format( entry.default_account, entry.company, self.name diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 94874894b0..3341f1ffd9 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -256,7 +256,7 @@ class PaymentEntry(AccountsController): self.validate_account_type(self.paid_to, ["Bank", "Cash"]) def validate_account_type(self, account, account_types): - account_type = frappe.db.get_value("Account", account, "account_type") + account_type = frappe.get_cached_value("Account", account, "account_type") # if account_type not in account_types: # frappe.throw(_("Account Type for {0} must be {1}").format(account, comma_or(account_types))) @@ -743,7 +743,7 @@ class PaymentEntry(AccountsController): def validate_transaction_reference(self): bank_account = self.paid_to if self.payment_type == "Receive" else self.paid_from - bank_account_type = frappe.db.get_value("Account", bank_account, "account_type") + bank_account_type = frappe.get_cached_value("Account", bank_account, "account_type") if bank_account_type == "Bank": if not self.reference_no or not self.reference_date: @@ -1321,7 +1321,7 @@ def split_invoices_based_on_payment_terms(outstanding_invoices): d.voucher_type, d.voucher_no, "payment_terms_template" ) if payment_term_template: - allocate_payment_based_on_payment_terms = frappe.db.get_value( + allocate_payment_based_on_payment_terms = frappe.get_cached_value( "Payment Terms Template", payment_term_template, "allocate_payment_based_on_payment_terms" ) if allocate_payment_based_on_payment_terms: @@ -1553,7 +1553,7 @@ def get_account_details(account, date, cost_center=None): { "account_currency": get_account_currency(account), "account_balance": account_balance, - "account_type": frappe.db.get_value("Account", account, "account_type"), + "account_type": frappe.get_cached_value("Account", account, "account_type"), } ) @@ -1722,9 +1722,9 @@ def get_payment_entry( if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked(): frappe.msgprint(_("{0} is on hold till {1}").format(doc.name, doc.release_date)) else: - if doc.doctype in ("Sales Invoice", "Purchase Invoice") and frappe.get_value( + if doc.doctype in ("Sales Invoice", "Purchase Invoice") and frappe.get_cached_value( "Payment Terms Template", - {"name": doc.payment_terms_template}, + doc.payment_terms_template, "allocate_payment_based_on_payment_terms", ): diff --git a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py index ab47b6151c..791de2570a 100644 --- a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py +++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py @@ -11,7 +11,7 @@ class PaymentGatewayAccount(Document): self.name = self.payment_gateway + " - " + self.currency def validate(self): - self.currency = frappe.db.get_value("Account", self.payment_account, "account_currency") + self.currency = frappe.get_cached_value("Account", self.payment_account, "account_currency") self.update_default_payment_gateway() self.set_as_default_if_not_set() diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py index bcbcb670fa..58691ab8d4 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py @@ -97,7 +97,7 @@ class PaymentLedgerEntry(Document): ) def validate_dimensions_for_pl_and_bs(self): - account_type = frappe.db.get_value("Account", self.account, "report_type") + account_type = frappe.get_cached_value("Account", self.account, "report_type") for dimension in get_checks_for_pl_and_bs_accounts(): if ( diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 29c497854c..8665b70956 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -53,7 +53,7 @@ class PaymentRequest(Document): def validate_currency(self): ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) - if self.payment_account and ref_doc.currency != frappe.db.get_value( + if self.payment_account and ref_doc.currency != frappe.get_cached_value( "Account", self.payment_account, "account_currency" ): frappe.throw(_("Transaction currency must be same as Payment Gateway currency")) 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 866a94d04b..ca98bee5c1 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -43,7 +43,7 @@ class PeriodClosingVoucher(AccountsController): make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name) def validate_account_head(self): - closing_account_type = frappe.db.get_value("Account", self.closing_account_head, "root_type") + closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type") if closing_account_type not in ["Liability", "Equity"]: frappe.throw( diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 54a3e934b2..81c19858c0 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -335,7 +335,8 @@ class POSInvoice(SalesInvoice): if ( self.change_amount and self.account_for_change_amount - and frappe.db.get_value("Account", self.account_for_change_amount, "company") != self.company + and frappe.get_cached_value("Account", self.account_for_change_amount, "company") + != self.company ): frappe.throw( _("The selected change account {} doesn't belongs to Company {}.").format( @@ -486,7 +487,7 @@ class POSInvoice(SalesInvoice): customer_price_list, customer_group, customer_currency = frappe.db.get_value( "Customer", self.customer, ["default_price_list", "customer_group", "default_currency"] ) - customer_group_price_list = frappe.db.get_value( + customer_group_price_list = frappe.get_cached_value( "Customer Group", customer_group, "default_price_list" ) selling_price_list = ( @@ -532,7 +533,7 @@ class POSInvoice(SalesInvoice): if not self.debit_to: self.debit_to = get_party_account("Customer", self.customer, self.company) - self.party_account_currency = frappe.db.get_value( + self.party_account_currency = frappe.get_cached_value( "Account", self.debit_to, "account_currency", cache=True ) if not self.due_date and self.customer: diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 882a374046..41be6f91a3 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -153,8 +153,8 @@ class PurchaseInvoice(BuyingController): def set_missing_values(self, for_validate=False): if not self.credit_to: self.credit_to = get_party_account("Supplier", self.supplier, self.company) - self.party_account_currency = frappe.db.get_value( - "Account", self.credit_to, "account_currency", cache=True + self.party_account_currency = frappe.get_cached_value( + "Account", self.credit_to, "account_currency" ) if not self.due_date: self.due_date = get_due_date( @@ -175,7 +175,7 @@ class PurchaseInvoice(BuyingController): if not self.credit_to: self.raise_missing_debit_credit_account_error("Supplier", self.supplier) - account = frappe.db.get_value( + account = frappe.get_cached_value( "Account", self.credit_to, ["account_type", "report_type", "account_currency"], as_dict=True ) @@ -673,7 +673,7 @@ class PurchaseInvoice(BuyingController): exchange_rate_map, net_rate_map = get_purchase_document_details(self) provisional_accounting_for_non_stock_items = cint( - frappe.db.get_value( + frappe.get_cached_value( "Company", self.company, "enable_provisional_accounting_for_non_stock_items" ) ) @@ -984,7 +984,7 @@ class PurchaseInvoice(BuyingController): asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate) base_asset_amount = flt(item.base_net_amount + item.item_tax_amount) - item_exp_acc_type = frappe.db.get_value("Account", item.expense_account, "account_type") + item_exp_acc_type = frappe.get_cached_value("Account", item.expense_account, "account_type") if not item.expense_account or item_exp_acc_type not in [ "Asset Received But Not Billed", "Fixed Asset", diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py index d9009bae4c..a3a5d627b7 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py @@ -27,7 +27,7 @@ class SalesTaxesandChargesTemplate(Document): def set_missing_values(self): for data in self.taxes: if data.charge_type == "On Net Total" and flt(data.rate) == 0.0: - data.rate = frappe.db.get_value("Account", data.account_head, "tax_rate") + data.rate = frappe.get_cached_value("Account", data.account_head, "tax_rate") def valdiate_taxes_and_charges_template(doc): diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 67cf644353..aa0e39b932 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -296,7 +296,7 @@ def get_default_price_list(party): return party.default_price_list if party.doctype == "Customer": - return frappe.db.get_value("Customer Group", party.customer_group, "default_price_list") + return frappe.get_cached_value("Customer Group", party.customer_group, "default_price_list") def set_price_list(party_details, party, party_type, given_price_list, pos=None): @@ -385,7 +385,7 @@ def get_party_account(party_type, party=None, company=None): existing_gle_currency = get_party_gle_currency(party_type, party, company) if existing_gle_currency: if account: - account_currency = frappe.db.get_value("Account", account, "account_currency", cache=True) + account_currency = frappe.get_cached_value("Account", account, "account_currency", cache=True) if (account and account_currency != existing_gle_currency) or not account: account = get_party_gle_account(party_type, party, company) @@ -402,7 +402,7 @@ def get_party_bank_account(party_type, party): def get_party_account_currency(party_type, party, company): def generator(): party_account = get_party_account(party_type, party, company) - return frappe.db.get_value("Account", party_account, "account_currency", cache=True) + return frappe.get_cached_value("Account", party_account, "account_currency", cache=True) return frappe.local_cache("party_account_currency", (party_type, party, company), generator) @@ -474,7 +474,7 @@ def validate_party_accounts(doc): else: companies.append(account.company) - party_account_currency = frappe.db.get_value( + party_account_currency = frappe.get_cached_value( "Account", account.account, "account_currency", cache=True ) if frappe.db.get_default("Company"): @@ -482,7 +482,9 @@ def validate_party_accounts(doc): "Company", frappe.db.get_default("Company"), "default_currency" ) else: - company_default_currency = frappe.db.get_value("Company", account.company, "default_currency") + company_default_currency = frappe.get_cached_value( + "Company", account.company, "default_currency" + ) validate_party_gle_currency(doc.doctype, doc.name, account.company, party_account_currency) @@ -801,7 +803,7 @@ def get_dashboard_info(party_type, party, loyalty_program=None): ) for d in companies: - company_default_currency = frappe.db.get_value("Company", d.company, "default_currency") + company_default_currency = frappe.get_cached_value("Company", d.company, "default_currency") party_account_currency = get_party_account_currency(party_type, party, d.company) if party_account_currency == company_default_currency: diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py index fbc1a69ddc..b0a0e05f09 100644 --- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py +++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py @@ -21,7 +21,7 @@ def execute(filters=None): if not filters.get("account"): return columns, [] - account_currency = frappe.db.get_value("Account", filters.account, "account_currency") + account_currency = frappe.get_cached_value("Account", filters.account, "account_currency") data = get_entries(filters) diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index 75e983afc0..d4f2011aa8 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -180,7 +180,7 @@ def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters = frappe._dict(filters or {}) if filters.include_default_book_entries: - company_fb = frappe.db.get_value("Company", company, "default_finance_book") + company_fb = frappe.get_cached_value("Company", company, "default_finance_book") cond = """ AND (finance_book in (%s, %s, '') OR finance_book IS NULL) """ % ( frappe.db.escape(filters.finance_book), diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 330e442a80..e93fb6138a 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -641,7 +641,7 @@ def set_gl_entries_by_account( "rgt": root_rgt, "company": d.name, "finance_book": filters.get("finance_book"), - "company_fb": frappe.db.get_value("Company", d.name, "default_finance_book"), + "company_fb": frappe.get_cached_value("Company", d.name, "default_finance_book"), }, as_dict=True, ) 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 cafe95b360..a257ba4ce5 100644 --- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py +++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py @@ -220,8 +220,8 @@ class PartyLedgerSummaryReport(object): if self.filters.party_type == "Customer": if self.filters.get("customer_group"): - lft, rgt = frappe.db.get_value( - "Customer Group", self.filters.get("customer_group"), ["lft", "rgt"] + lft, rgt = frappe.get_cached_value( + "Customer Group", self.filters.get("customer_group"), ["lft" "rgt"] ) conditions.append( diff --git a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py index ecad9f104f..5939a26deb 100644 --- a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py +++ b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py @@ -90,7 +90,7 @@ def set_gl_entries_by_account(dimension_list, filters, account, gl_entries_by_ac gl_filters["dimensions"] = set(dimension_list) if filters.get("include_default_book_entries"): - gl_filters["company_fb"] = frappe.db.get_value( + gl_filters["company_fb"] = frappe.get_cached_value( "Company", filters.company, "default_finance_book" ) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 36825771f8..8c6fe43a93 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -440,7 +440,7 @@ def set_gl_entries_by_account( } if filters.get("include_default_book_entries"): - gl_filters["company_fb"] = frappe.db.get_value("Company", company, "default_finance_book") + gl_filters["company_fb"] = frappe.get_cached_value("Company", company, "default_finance_book") for key, value in filters.items(): if value: diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 82f38dacd2..615db29267 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -174,7 +174,7 @@ def get_gl_entries(filters, accounting_dimensions): order_by_statement = "order by account, posting_date, creation" if filters.get("include_default_book_entries"): - filters["company_fb"] = frappe.db.get_value( + filters["company_fb"] = frappe.get_cached_value( "Company", filters.get("company"), "default_finance_book" ) @@ -287,7 +287,7 @@ def get_accounts_with_children(accounts): all_accounts = [] for d in accounts: if frappe.db.exists("Account", d): - lft, rgt = frappe.db.get_value("Account", d, ["lft", "rgt"]) + lft, rgt = frappe.get_cached_value("Account", d, ["lft", "rgt"]) children = frappe.get_all("Account", filters={"lft": [">=", lft], "rgt": ["<=", rgt]}) all_accounts += [c.name for c in children] else: diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 6d2cd8ed41..3af01fde7d 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -38,7 +38,7 @@ def validate_filters(filters): if not filters.fiscal_year: frappe.throw(_("Fiscal Year {0} is required").format(filters.fiscal_year)) - fiscal_year = frappe.db.get_value( + fiscal_year = frappe.get_cached_value( "Fiscal Year", filters.fiscal_year, ["year_start_date", "year_end_date"], as_dict=True ) if not fiscal_year: @@ -177,7 +177,7 @@ def get_rootwise_opening_balances(filters, report_type): "year_start_date": filters.year_start_date, "project": filters.project, "finance_book": filters.finance_book, - "company_fb": frappe.db.get_value("Company", filters.company, "default_finance_book"), + "company_fb": frappe.get_cached_value("Company", filters.company, "default_finance_book"), } if accounting_dimensions: diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index d7bf991688..e9ff1d917a 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -975,7 +975,7 @@ def get_account_balances(accounts, company): def create_payment_gateway_account(gateway, payment_channel="Email"): from erpnext.setup.setup_wizard.operations.install_fixtures import create_bank_account - company = frappe.db.get_value("Global Defaults", None, "default_company") + company = frappe.get_cached_value("Global Defaults", None, "default_company") if not company: return From 010bd9c558b67223224e6d7840b8abe7b6aa8d0e Mon Sep 17 00:00:00 2001 From: Daizy Modi Date: Thu, 3 Nov 2022 14:36:48 +0530 Subject: [PATCH 0456/1047] fix: remove cache param --- erpnext/accounts/doctype/pos_invoice/pos_invoice.py | 2 +- erpnext/accounts/party.py | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 81c19858c0..b543016eaa 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -534,7 +534,7 @@ class POSInvoice(SalesInvoice): if not self.debit_to: self.debit_to = get_party_account("Customer", self.customer, self.company) self.party_account_currency = frappe.get_cached_value( - "Account", self.debit_to, "account_currency", cache=True + "Account", self.debit_to, "account_currency" ) if not self.due_date and self.customer: self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index aa0e39b932..baeed03a0f 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -385,7 +385,7 @@ def get_party_account(party_type, party=None, company=None): existing_gle_currency = get_party_gle_currency(party_type, party, company) if existing_gle_currency: if account: - account_currency = frappe.get_cached_value("Account", account, "account_currency", cache=True) + account_currency = frappe.get_cached_value("Account", account, "account_currency") if (account and account_currency != existing_gle_currency) or not account: account = get_party_gle_account(party_type, party, company) @@ -402,7 +402,7 @@ def get_party_bank_account(party_type, party): def get_party_account_currency(party_type, party, company): def generator(): party_account = get_party_account(party_type, party, company) - return frappe.get_cached_value("Account", party_account, "account_currency", cache=True) + return frappe.get_cached_value("Account", party_account, "account_currency") return frappe.local_cache("party_account_currency", (party_type, party, company), generator) @@ -474,9 +474,7 @@ def validate_party_accounts(doc): else: companies.append(account.company) - party_account_currency = frappe.get_cached_value( - "Account", account.account, "account_currency", cache=True - ) + party_account_currency = frappe.get_cached_value("Account", account.account, "account_currency") if frappe.db.get_default("Company"): company_default_currency = frappe.get_cached_value( "Company", frappe.db.get_default("Company"), "default_currency" From e5b19e3f700daf374c90dbcacef3740a74c42f82 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 2 Nov 2022 20:38:07 +0530 Subject: [PATCH 0457/1047] fix: auto increment qty if item table has no items --- erpnext/public/js/controllers/transaction.js | 7 +++++++ erpnext/public/js/utils/barcode_scanner.js | 9 +++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 7fecb18fad..96daeef9db 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -341,6 +341,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe this.set_dynamic_labels(); this.setup_sms(); this.setup_quality_inspection(); + this.validate_has_items(); } scan_barcode() { @@ -348,6 +349,12 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe barcode_scanner.process_scan(); } + validate_has_items () { + let table = this.frm.doc.items; + this.frm.has_items = (table && table.length + && table[0].qty && table[0].item_code); + } + apply_default_taxes() { var me = this; var taxes_and_charges_field = frappe.meta.get_docfield(me.frm.doc.doctype, "taxes_and_charges", diff --git a/erpnext/public/js/utils/barcode_scanner.js b/erpnext/public/js/utils/barcode_scanner.js index 5d3a16114a..a4f74bdaee 100644 --- a/erpnext/public/js/utils/barcode_scanner.js +++ b/erpnext/public/js/utils/barcode_scanner.js @@ -103,6 +103,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { row = frappe.model.add_child(this.frm.doc, cur_grid.doctype, this.items_table_name); // trigger any row add triggers defined on child table. this.frm.script_manager.trigger(`${this.items_table_name}_add`, row.doctype, row.name); + this.frm.has_items = false; } if (this.is_duplicate_serial_no(row, serial_no)) { @@ -157,8 +158,10 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { frappe.prompt(__("Please enter quantity for item {0}", [item_code]), ({value}) => { increment(value).then((value) => resolve(value)); }); - } else { + } else if (this.frm.has_items) { this.prepare_item_for_scan(row, item_code, barcode, batch_no, serial_no); + } else { + increment().then((value) => resolve(value)); } }); } @@ -207,7 +210,6 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { return; } - debugger if (e.target.value) { this.scan_api_call(e.target.value, (r) => { if (r.message) { @@ -280,7 +282,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { } update_dialog_values(scanned_item, r) { - const {item_code, barcode, batch_no, serial_no, uom} = r.message; + const {item_code, barcode, batch_no, serial_no} = r.message; this.dialog.set_value("barcode_scanner", ""); if (item_code === scanned_item && @@ -357,7 +359,6 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { } async set_batch_no(row, batch_no) { - debugger if (batch_no && frappe.meta.has_field(row.doctype, this.batch_no_field)) { await frappe.model.set_value(row.doctype, row.name, this.batch_no_field, batch_no); } From e2ad785422f83095e07043579d6e0abf46e49ebe Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 4 Nov 2022 01:03:20 +0530 Subject: [PATCH 0458/1047] chore: Hide tax withholding net total field --- .../purchase_invoice/purchase_invoice.json | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index d4c6d04e9d..370c0fc960 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -91,7 +91,6 @@ "section_break_44", "apply_discount_on", "base_discount_amount", - "additional_discount_account", "column_break_46", "additional_discount_percentage", "discount_amount", @@ -1424,22 +1423,24 @@ "read_only": 1 }, { - "default": "0", - "fieldname": "tax_withholding_net_total", - "fieldtype": "Currency", - "label": "Tax Withholding Net Total", - "no_copy": 1, - "options": "currency", - "read_only": 1 + "default": "0", + "fieldname": "tax_withholding_net_total", + "fieldtype": "Currency", + "hidden": 1, + "label": "Tax Withholding Net Total", + "no_copy": 1, + "options": "currency", + "read_only": 1 }, { - "fieldname": "base_tax_withholding_net_total", - "fieldtype": "Currency", - "label": "Base Tax Withholding Net Total", - "no_copy": 1, - "options": "currency", - "print_hide": 1, - "read_only": 1 + "fieldname": "base_tax_withholding_net_total", + "fieldtype": "Currency", + "hidden": 1, + "label": "Base Tax Withholding Net Total", + "no_copy": 1, + "options": "currency", + "print_hide": 1, + "read_only": 1 }, { "collapsible_depends_on": "tax_withheld_vouchers", @@ -1539,7 +1540,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2022-10-11 13:04:44.304389", + "modified": "2022-11-04 01:02:44.544878", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", @@ -1603,4 +1604,4 @@ "timeline_field": "supplier", "title_field": "title", "track_changes": 1 -} +} \ No newline at end of file From 153675e52ac9230c61ca4d8d57d7f65d8e8008e3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 4 Nov 2022 01:03:45 +0530 Subject: [PATCH 0459/1047] chore: Update patch --- erpnext/patches/v14_0/update_tds_fields.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v14_0/update_tds_fields.py b/erpnext/patches/v14_0/update_tds_fields.py index 513acb2523..fd33d5450d 100644 --- a/erpnext/patches/v14_0/update_tds_fields.py +++ b/erpnext/patches/v14_0/update_tds_fields.py @@ -1,4 +1,5 @@ import frappe +from frappe.utils import nowdate from erpnext.accounts.utils import get_fiscal_year @@ -6,7 +7,7 @@ from erpnext.accounts.utils import get_fiscal_year def execute(): # Only do for current fiscal year, no need to repost for all years for company in frappe.get_all("Company"): - fiscal_year_details = get_fiscal_year(company=company.name, as_dict=True) + fiscal_year_details = get_fiscal_year(date=nowdate(), company=company.name, as_dict=True) purchase_invoice = frappe.qb.DocType("Purchase Invoice") From ad0dd693ac4e5441f26f2ab3af1c6aa1d55426df Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Thu, 3 Nov 2022 13:36:01 -0600 Subject: [PATCH 0460/1047] chore: add translation function to Bank Reconciliation Tool related files chore: add translation function to Bank Reconciliation Tool related files --- .../bank_reconciliation_tool.js | 3 ++ .../bank_reconciliation_tool.json | 5 ++- .../bank_statement_import.js | 2 +- .../data_table_manager.js | 24 ++++++------- .../dialog_manager.js | 34 ++++++------------- .../bank_reconciliation_tool/number_card.js | 6 ++-- 6 files changed, 32 insertions(+), 42 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js index 46ba27c004..28e79b5d2c 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js @@ -12,6 +12,9 @@ frappe.ui.form.on("Bank Reconciliation Tool", { }, }; }); + let no_bank_transactions_text = + `
${__("No Matching Bank Transactions Found")}
` + set_field_options("no_bank_transactions", no_bank_transactions_text); }, onload: function (frm) { diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json index b643e6e091..f666101d3f 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json @@ -81,8 +81,7 @@ }, { "fieldname": "no_bank_transactions", - "fieldtype": "HTML", - "options": "
No Matching Bank Transactions Found
" + "fieldtype": "HTML" } ], "hide_toolbar": 1, @@ -109,4 +108,4 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js index f74562086e..04af32346b 100644 --- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js @@ -100,7 +100,7 @@ frappe.ui.form.on("Bank Statement Import", { if (frm.doc.status.includes("Success")) { frm.add_custom_button( - __("Go to {0} List", [frm.doc.reference_doctype]), + __("Go to {0} List", [__(frm.doc.reference_doctype)]), () => frappe.set_route("List", frm.doc.reference_doctype) ); } diff --git a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js index 5bb58faf2f..9ef8ce6b63 100644 --- a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js +++ b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js @@ -30,28 +30,28 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager { get_dt_columns() { this.columns = [ { - name: "Date", + name: __("Date"), editable: false, width: 100, }, { - name: "Party Type", + name: __("Party Type"), editable: false, width: 95, }, { - name: "Party", + name: __("Party"), editable: false, width: 100, }, { - name: "Description", + name: __("Description"), editable: false, width: 350, }, { - name: "Deposit", + name: __("Deposit"), editable: false, width: 100, format: (value) => @@ -60,7 +60,7 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager { "", }, { - name: "Withdrawal", + name: __("Withdrawal"), editable: false, width: 100, format: (value) => @@ -69,26 +69,26 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager { "", }, { - name: "Unallocated Amount", + name: __("Unallocated Amount"), editable: false, width: 100, format: (value) => - "" + + "" + format_currency(value, this.currency) + "", }, { - name: "Reference Number", + name: __("Reference Number"), editable: false, width: 140, }, { - name: "Actions", + name: __("Actions"), editable: false, sortable: false, focusable: false, dropdown: false, - width: 80, + width: 100, }, ]; } @@ -118,7 +118,7 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager { row["reference_number"], `