diff --git a/erpnext/accounts/doctype/item_tax_template/test_records.json b/erpnext/accounts/doctype/item_tax_template/test_records.json
index db540e86aac..4d9537d4b89 100644
--- a/erpnext/accounts/doctype/item_tax_template/test_records.json
+++ b/erpnext/accounts/doctype/item_tax_template/test_records.json
@@ -2,6 +2,7 @@
{
"doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 10",
+ "company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
@@ -14,6 +15,7 @@
{
"doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 12",
+ "company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
@@ -26,6 +28,7 @@
{
"doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 15",
+ "company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
@@ -38,6 +41,7 @@
{
"doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 20",
+ "company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
@@ -50,6 +54,7 @@
{
"doctype": "Item Tax Template",
"title": "_Test Item Tax Template 1",
+ "company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index e1f1b9b4dfc..dc27cfb3342 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -11,6 +11,7 @@ import json
from six import string_types
import frappe
+from erpnext.accounts.doctype.pricing_rule.pricing_rule import set_transaction_type
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
from erpnext.stock.get_item_details import get_conversion_factor, get_default_income_account
@@ -418,9 +419,28 @@ def apply_pricing_rule_on_transaction(doc):
values = {}
conditions = get_other_conditions(conditions, values, doc)
- pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule`
- where {conditions} and `tabPricing Rule`.disable = 0
- """.format(conditions = conditions), values, as_dict=1)
+ args = frappe._dict({
+ 'doctype': doc.doctype,
+ 'transaction_type': None,
+ })
+ set_transaction_type(args)
+ tran_type_condition = '{} = 1'.format(args.transaction_type)
+
+ sql = """
+ SELECT
+ `tabPricing Rule`.*
+ FROM
+ `tabPricing Rule`
+ WHERE
+ {conditions} and
+ {tran_type_condition} and
+ `tabPricing Rule`.disable = 0
+ """.format(
+ conditions=conditions,
+ tran_type_condition=tran_type_condition,
+ )
+
+ pricing_rules = frappe.db.sql(sql, values, as_dict=1)
if pricing_rules:
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
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 a8dfab6a54b..ff1dbedbd31 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -1,5 +1,4 @@
{
- "actions": [],
"autoname": "hash",
"creation": "2013-05-22 12:43:10",
"doctype": "DocType",
@@ -86,6 +85,7 @@
"item_tax_rate",
"bom",
"include_exploded_items",
+ "purchase_invoice_item",
"col_break6",
"purchase_order",
"po_detail",
@@ -764,12 +764,21 @@
"label": "Asset Category",
"options": "Asset Category",
"read_only": 1
+ },
+ {
+ "depends_on": "eval:parent.update_stock == 1",
+ "fieldname": "purchase_invoice_item",
+ "fieldtype": "Data",
+ "ignore_user_permissions": 1,
+ "label": "Purchase Invoice Item",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
- "links": [],
- "modified": "2020-04-07 18:34:35.104178",
+ "modified": "2020-06-30 16:48:01.398356",
"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 b2294e4318f..49deebd3c6c 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -1,5 +1,4 @@
{
- "actions": [],
"autoname": "hash",
"creation": "2013-06-04 11:02:19",
"doctype": "DocType",
@@ -87,6 +86,7 @@
"edit_references",
"sales_order",
"so_detail",
+ "sales_invoice_item",
"column_break_74",
"delivery_note",
"dn_detail",
@@ -783,12 +783,21 @@
"fieldtype": "Link",
"label": "Finance Book",
"options": "Finance Book"
+ },
+ {
+ "depends_on": "eval:parent.update_stock == 1",
+ "fieldname": "sales_invoice_item",
+ "fieldtype": "Data",
+ "ignore_user_permissions": 1,
+ "label": "Sales Invoice Item",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
- "links": [],
- "modified": "2019-12-04 12:22:38.517710",
+ "modified": "2020-06-30 16:47:37.650996",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 0973f165232..c7e6bcf4d65 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -182,7 +182,7 @@ class BuyingController(StockController):
if item.item_code and item.qty and item.item_code in stock_and_asset_items:
item_proportion = flt(item.base_net_amount) / stock_and_asset_items_amount if stock_and_asset_items_amount \
else flt(item.qty) / stock_and_asset_items_qty
-
+
if i == (last_item_idx - 1):
item.item_tax_amount = flt(valuation_amount_adjustment,
self.precision("item_tax_amount", item))
@@ -540,9 +540,19 @@ class BuyingController(StockController):
"serial_no": cstr(d.serial_no).strip()
})
if self.is_return:
- original_incoming_rate = frappe.db.get_value("Stock Ledger Entry",
- {"voucher_type": "Purchase Receipt", "voucher_no": self.return_against,
- "item_code": d.item_code}, "incoming_rate")
+ filters = {
+ "voucher_type": self.doctype,
+ "voucher_no": self.return_against,
+ "item_code": d.item_code
+ }
+
+ if (self.doctype == "Purchase Invoice" and self.update_stock
+ and d.get("purchase_invoice_item")):
+ filters["voucher_detail_no"] = d.purchase_invoice_item
+ elif self.doctype == "Purchase Receipt" and d.get("purchase_receipt_item"):
+ filters["voucher_detail_no"] = d.purchase_receipt_item
+
+ original_incoming_rate = frappe.db.get_value("Stock Ledger Entry", filters, "incoming_rate")
sle.update({
"outgoing_rate": original_incoming_rate
@@ -728,7 +738,7 @@ class BuyingController(StockController):
if delete_asset and is_auto_create_enabled:
# need to delete movements to delete assets otherwise throws link exists error
movements = frappe.db.sql(
- """SELECT asm.name
+ """SELECT asm.name
FROM `tabAsset Movement` asm, `tabAsset Movement Item` asm_item
WHERE asm_item.parent=asm.name and asm_item.asset=%s""", asset.name, as_dict=1)
for movement in movements:
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 90c67f1e521..18b5daf128a 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -278,6 +278,8 @@ def make_return_doc(doctype, source_name, target_doc=None):
target_doc.rejected_warehouse = source_doc.rejected_warehouse
target_doc.po_detail = source_doc.po_detail
target_doc.pr_detail = source_doc.pr_detail
+ target_doc.purchase_invoice_item = source_doc.name
+
elif doctype == "Delivery Note":
target_doc.against_sales_order = source_doc.against_sales_order
target_doc.against_sales_invoice = source_doc.against_sales_invoice
@@ -287,12 +289,14 @@ def make_return_doc(doctype, source_name, target_doc=None):
target_doc.dn_detail = source_doc.name
if default_warehouse_for_sales_return:
target_doc.warehouse = default_warehouse_for_sales_return
+
elif doctype == "Sales Invoice":
target_doc.sales_order = source_doc.sales_order
target_doc.delivery_note = source_doc.delivery_note
target_doc.so_detail = source_doc.so_detail
target_doc.dn_detail = source_doc.dn_detail
target_doc.expense_account = source_doc.expense_account
+ target_doc.sales_invoice_item = source_doc.name
if default_warehouse_for_sales_return:
target_doc.warehouse = default_warehouse_for_sales_return
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index c25ad060674..1399654ffd2 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -216,7 +216,9 @@ class SellingController(StockController):
'target_warehouse': p.target_warehouse,
'company': self.company,
'voucher_type': self.doctype,
- 'allow_zero_valuation': d.allow_zero_valuation_rate
+ 'allow_zero_valuation': d.allow_zero_valuation_rate,
+ 'sales_invoice_item': d.get("sales_invoice_item"),
+ 'delivery_note_item': d.get("dn_detail")
}))
else:
il.append(frappe._dict({
@@ -232,7 +234,9 @@ class SellingController(StockController):
'target_warehouse': d.target_warehouse,
'company': self.company,
'voucher_type': self.doctype,
- 'allow_zero_valuation': d.allow_zero_valuation_rate
+ 'allow_zero_valuation': d.allow_zero_valuation_rate,
+ 'sales_invoice_item': d.get("sales_invoice_item"),
+ 'delivery_note_item': d.get("dn_detail")
}))
return il
@@ -301,7 +305,11 @@ class SellingController(StockController):
d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0
return_rate = 0
if cint(self.is_return) and self.return_against and self.docstatus==1:
- return_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against)
+ against_document_no = (d.get("sales_invoice_item")
+ if self.doctype == "Sales Invoice" else d.get("delivery_note_item"))
+
+ return_rate = self.get_incoming_rate_for_sales_return(d.item_code,
+ self.return_against, against_document_no)
# On cancellation or if return entry submission, make stock ledger entry for
# target warehouse first, to update serial no values properly
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index ff6ac420208..29f389edfec 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -297,14 +297,19 @@ class StockController(AccountsController):
return serialized_items
- def get_incoming_rate_for_sales_return(self, item_code, against_document):
+ def get_incoming_rate_for_sales_return(self, item_code, against_document, against_document_no=None):
incoming_rate = 0.0
+ cond = ''
if against_document and item_code:
+ if against_document_no:
+ cond = " and voucher_detail_no = %s" %(frappe.db.escape(against_document_no))
+
incoming_rate = frappe.db.sql("""select abs(stock_value_difference / actual_qty)
from `tabStock Ledger Entry`
where voucher_type = %s and voucher_no = %s
- and item_code = %s limit 1""",
+ and item_code = %s {0} limit 1""".format(cond),
(self.doctype, against_document, item_code))
+
incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
return incoming_rate
diff --git a/erpnext/controllers/tests/test_mapper.py b/erpnext/controllers/tests/test_mapper.py
index d02308d8f21..66459fdbf8a 100644
--- a/erpnext/controllers/tests/test_mapper.py
+++ b/erpnext/controllers/tests/test_mapper.py
@@ -13,14 +13,12 @@ class TestMapper(unittest.TestCase):
'''Test mapping of multiple source docs on a single target doc'''
make_test_records("Item")
- items = frappe.get_all("Item", fields = ["name", "item_code"], filters = {'is_sales_item': 1, 'has_variants': 0})
- customers = frappe.get_all("Customer")
- if items and customers:
- # Make source docs (quotations) and a target doc (sales order)
- customer = random.choice(customers).name
- qtn1, item_list_1 = self.make_quotation(items, customer)
- qtn2, item_list_2 = self.make_quotation(items, customer)
- so, item_list_3 = self.make_sales_order()
+ items = ['_Test Item', '_Test Item 2', '_Test FG Item']
+
+ # Make source docs (quotations) and a target doc (sales order)
+ qtn1, item_list_1 = self.make_quotation(items, '_Test Customer')
+ qtn2, item_list_2 = self.make_quotation(items, '_Test Customer')
+ so, item_list_3 = self.make_sales_order()
# Map source docs to target with corresponding mapper method
method = "erpnext.selling.doctype.quotation.quotation.make_sales_order"
@@ -28,18 +26,12 @@ class TestMapper(unittest.TestCase):
# Assert that all inserted items are present in updated sales order
src_items = item_list_1 + item_list_2 + item_list_3
- self.assertEqual(set([d.item_code for d in src_items]),
+ self.assertEqual(set([d for d in src_items]),
set([d.item_code for d in updated_so.items]))
- def get_random_items(self, items, limit):
- '''Get a number of random items from a list of given items'''
- random_items = []
- for i in range(0, limit):
- random_items.append(random.choice(items))
- return random_items
- def make_quotation(self, items, customer):
- item_list = self.get_random_items(items, 3)
+ def make_quotation(self, item_list, customer):
+
qtn = frappe.get_doc({
"doctype": "Quotation",
"quotation_to": "Customer",
@@ -49,7 +41,7 @@ class TestMapper(unittest.TestCase):
"valid_till" : add_months(nowdate(), 1)
})
for item in item_list:
- qtn.append("items", {"qty": "2", "item_code": item.item_code})
+ qtn.append("items", {"qty": "2", "item_code": item})
qtn.submit()
return qtn, item_list
@@ -60,7 +52,7 @@ class TestMapper(unittest.TestCase):
"base_rate": 100.0,
"description": "CPU",
"doctype": "Sales Order Item",
- "item_code": "_Test Item Home Desktop 100",
+ "item_code": "_Test Item",
"item_name": "CPU",
"parentfield": "items",
"qty": 10.0,
@@ -72,4 +64,4 @@ class TestMapper(unittest.TestCase):
})
so = frappe.get_doc(frappe.get_test_records('Sales Order')[0])
so.insert(ignore_permissions=True)
- return so, [item]
+ return so, [item.item_code]
diff --git a/erpnext/controllers/tests/test_qty_based_taxes.py b/erpnext/controllers/tests/test_qty_based_taxes.py
index fd9936bae99..aaeac5d9399 100644
--- a/erpnext/controllers/tests/test_qty_based_taxes.py
+++ b/erpnext/controllers/tests/test_qty_based_taxes.py
@@ -30,6 +30,7 @@ class TestTaxes(unittest.TestCase):
self.item_tax_template = frappe.get_doc({
'doctype': 'Item Tax Template',
'title': uuid4(),
+ 'company': self.company.name,
'taxes': [
{
'tax_type': self.account.name,
diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js
index 13079172fe8..9ca46912fa7 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.js
+++ b/erpnext/crm/doctype/opportunity/opportunity.js
@@ -60,12 +60,18 @@ frappe.ui.form.on("Opportunity", {
opportunity_from: function(frm) {
frm.toggle_reqd("party_name", frm.doc.opportunity_from);
+ frm.trigger("setup_opportunity_from");
+ frm.set_value("party_name","");
+ },
+
+ setup_opportunity_from: function(frm) {
+ frm.trigger('setup_queries');
frm.trigger("set_dynamic_field_label");
},
refresh: function(frm) {
var doc = frm.doc;
- frm.events.opportunity_from(frm);
+ frm.trigger("setup_opportunity_from");
frm.trigger('toggle_mandatory');
erpnext.toggle_naming_series();
diff --git a/erpnext/education/api.py b/erpnext/education/api.py
index 1a19716b508..d79a143ea20 100644
--- a/erpnext/education/api.py
+++ b/erpnext/education/api.py
@@ -151,7 +151,7 @@ def get_fee_components(fee_structure):
:param fee_structure: Fee Structure.
"""
if fee_structure:
- fs = frappe.get_list("Fee Component", fields=["fees_category", "amount"] , filters={"parent": fee_structure}, order_by= "idx")
+ fs = frappe.get_list("Fee Component", fields=["fees_category", "description", "amount"] , filters={"parent": fee_structure}, order_by= "idx")
return fs
@@ -363,9 +363,9 @@ def get_current_enrollment(student, academic_year=None):
select
name as program_enrollment, student_name, program, student_batch_name as student_batch,
student_category, academic_term, academic_year
- from
+ from
`tabProgram Enrollment`
- where
+ where
student = %s and academic_year = %s
order by creation''', (student, current_academic_year), as_dict=1)
diff --git a/erpnext/education/doctype/fees/fees.js b/erpnext/education/doctype/fees/fees.js
index 17ef44954b1..ba9dafce1e1 100644
--- a/erpnext/education/doctype/fees/fees.js
+++ b/erpnext/education/doctype/fees/fees.js
@@ -161,6 +161,7 @@ frappe.ui.form.on("Fees", {
$.each(r.message, function(i, d) {
var row = frappe.model.add_child(frm.doc, "Fee Component", "components");
row.fees_category = d.fees_category;
+ row.description = d.description;
row.amount = d.amount;
});
}
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index 452aa74281e..774aa51daa1 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -776,10 +776,10 @@ class SalarySlip(TransactionBase):
# other taxes and charges on income tax
for d in tax_slab.other_taxes_and_charges:
- if flt(d.min_taxable_income) and flt(d.min_taxable_income) > tax_amount:
+ if flt(d.min_taxable_income) and flt(d.min_taxable_income) > annual_taxable_earning:
continue
- if flt(d.max_taxable_income) and flt(d.max_taxable_income) < tax_amount:
+ if flt(d.max_taxable_income) and flt(d.max_taxable_income) < annual_taxable_earning:
continue
tax_amount += tax_amount * flt(d.percent) / 100
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json
index c797b7ea77c..11925681dfd 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json
@@ -701,7 +701,7 @@
"columns": 0,
"default": "Draft",
"fieldname": "status",
- "fieldtype": "Data",
+ "fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -1001,7 +1001,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-08-21 14:44:44.911402",
+ "modified": "2020-07-15 14:44:44.911402",
"modified_by": "Administrator",
"module": "Maintenance",
"name": "Maintenance Visit",
diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js
index d5f83d60298..59edcf1d7e6 100644
--- a/erpnext/public/js/financial_statements.js
+++ b/erpnext/public/js/financial_statements.js
@@ -3,7 +3,7 @@ frappe.provide("erpnext.financial_statements");
erpnext.financial_statements = {
"filters": get_filters(),
"formatter": function(value, row, column, data, default_formatter) {
- if (column.fieldname=="account") {
+ if (data && column.fieldname=="account") {
value = data.account_name || value;
column.link_onclick =
@@ -13,7 +13,7 @@ erpnext.financial_statements = {
value = default_formatter(value, row, column, data);
- if (!data.parent_account) {
+ if (data && !data.parent_account) {
value = $(`${value}`);
var $value = $(value).css("font-weight", "bold");
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index e78ab9fb690..f29e42f8940 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -333,8 +333,8 @@ erpnext.SerialNoBatchSelector = Class.extend({
};
},
change: function () {
- let val = this.get_value();
- if (val.length === 0) {
+ const batch_no = this.get_value();
+ if (!batch_no) {
this.grid_row.on_grid_fields_dict
.available_qty.set_value(0);
return;
@@ -354,14 +354,11 @@ erpnext.SerialNoBatchSelector = Class.extend({
return;
}
- let batch_number = me.item.batch_no ||
- this.grid_row.on_grid_fields_dict.batch_no.get_value();
-
if (me.warehouse_details.name) {
frappe.call({
method: 'erpnext.stock.doctype.batch.batch.get_batch_qty',
args: {
- batch_no: batch_number,
+ batch_no,
warehouse: me.warehouse_details.name,
item_code: me.item_code
},
diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py
index 222dfa1eb78..a3ed4cebb12 100644
--- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py
+++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py
@@ -7,6 +7,8 @@ from frappe import _
from frappe.utils import flt
from frappe.model.meta import get_field_precision
from frappe.utils.xlsxutils import handle_html
+from six import iteritems
+import json
def execute(filters=None):
return _execute(filters)
@@ -21,21 +23,24 @@ def _execute(filters=None):
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
data = []
+ added_item = []
for d in item_list:
- row = [d.gst_hsn_code, d.description, d.stock_uom, d.stock_qty]
- total_tax = 0
- for tax in tax_columns:
- item_tax = itemised_tax.get(d.name, {}).get(tax, {})
- total_tax += flt(item_tax.get("tax_amount"))
+ if (d.parent, d.item_code) not in added_item:
+ row = [d.gst_hsn_code, d.description, d.stock_uom, d.stock_qty]
+ total_tax = 0
+ for tax in tax_columns:
+ item_tax = itemised_tax.get((d.parent, d.item_code), {}).get(tax, {})
+ total_tax += flt(item_tax.get("tax_amount", 0))
- row += [d.base_net_amount + total_tax]
- row += [d.base_net_amount]
+ row += [d.base_net_amount + total_tax]
+ row += [d.base_net_amount]
- for tax in tax_columns:
- item_tax = itemised_tax.get(d.name, {}).get(tax, {})
- row += [item_tax.get("tax_amount", 0)]
+ for tax in tax_columns:
+ item_tax = itemised_tax.get((d.parent, d.item_code), {}).get(tax, {})
+ row += [item_tax.get("tax_amount", 0)]
- data.append(row)
+ data.append(row)
+ added_item.append((d.parent, d.item_code))
if data:
data = get_merged_data(columns, data) # merge same hsn code data
return columns, data
@@ -103,7 +108,7 @@ def get_items(filters):
match_conditions = " and {0} ".format(match_conditions)
- return frappe.db.sql("""
+ items = frappe.db.sql("""
select
`tabSales Invoice Item`.name, `tabSales Invoice Item`.base_price_list_rate,
`tabSales Invoice Item`.gst_hsn_code, `tabSales Invoice Item`.stock_qty,
@@ -118,10 +123,9 @@ def get_items(filters):
""" % (conditions, match_conditions), filters, as_dict=1)
+ return items
-def get_tax_accounts(item_list, columns, company_currency,
- doctype="Sales Invoice", tax_doctype="Sales Taxes and Charges"):
- import json
+def get_tax_accounts(item_list, columns, company_currency, doctype="Sales Invoice", tax_doctype="Sales Taxes and Charges"):
item_row_map = {}
tax_columns = []
invoice_item_row = {}
@@ -171,7 +175,7 @@ def get_tax_accounts(item_list, columns, company_currency,
for d in item_row_map.get(parent, {}).get(item_code, []):
item_tax_amount = tax_amount
if item_tax_amount:
- itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
+ itemised_tax.setdefault((parent, item_code), {})[description] = frappe._dict({
"tax_amount": flt(item_tax_amount, tax_amount_precision)
})
except ValueError:
@@ -179,42 +183,32 @@ def get_tax_accounts(item_list, columns, company_currency,
tax_columns.sort()
for desc in tax_columns:
- columns.append(desc + " Amount:Currency/currency:160")
+ columns.append({
+ "label": desc,
+ "fieldname": frappe.scrub(desc),
+ "fieldtype": "Float",
+ "width": 110
+ })
- # columns += ["Total Amount:Currency/currency:110"]
return itemised_tax, tax_columns
def get_merged_data(columns, data):
merged_hsn_dict = {} # to group same hsn under one key and perform row addition
- add_column_index = [] # store index of columns that needs to be added
- tax_col = len(get_columns())
- fields_to_merge = ["stock_qty", "total_amount", "taxable_amount"] # columns for which index needs to be found
-
- for i,d in enumerate(columns):
- # check if fieldname in to_merge list and ignore tax-columns
- if i < tax_col and d["fieldname"] in fields_to_merge:
- add_column_index.append(i)
+ result = []
for row in data:
- if row[0] in merged_hsn_dict:
- to_add_row = merged_hsn_dict.get(row[0])
+ merged_hsn_dict.setdefault(row[0], {})
+ for i, d in enumerate(columns):
+ if d['fieldtype'] not in ('Int', 'Float', 'Currency'):
+ merged_hsn_dict[row[0]][d['fieldname']] = row[i]
+ else:
+ if merged_hsn_dict.get(row[0], {}).get(d['fieldname'], ''):
+ merged_hsn_dict[row[0]][d['fieldname']] += row[i]
+ else:
+ merged_hsn_dict[row[0]][d['fieldname']] = row[i]
- # add columns from the add_column_index table
- for k in add_column_index:
- to_add_row[k] += row[k]
+ for key, value in iteritems(merged_hsn_dict):
+ result.append(value)
- # add tax columns
- for k in range(len(columns)):
- if tax_col <= k < len(columns):
- to_add_row[k] += row[k]
-
- # update hsn dict with the newly added data
- merged_hsn_dict[row[0]] = to_add_row
- else:
- merged_hsn_dict[row[0]] = row
-
- # extract data rows to be displayed in report
- data = [merged_hsn_dict[d] for d in merged_hsn_dict]
-
- return data
+ return result
diff --git a/erpnext/selling/doctype/sales_order/test_records.json b/erpnext/selling/doctype/sales_order/test_records.json
index 6cbd6c2fc17..8a090e6d3d3 100644
--- a/erpnext/selling/doctype/sales_order/test_records.json
+++ b/erpnext/selling/doctype/sales_order/test_records.json
@@ -1,39 +1,39 @@
[
{
"advance_paid": 0.0,
- "company": "_Test Company",
- "conversion_rate": 1.0,
- "currency": "INR",
- "customer": "_Test Customer",
- "customer_group": "_Test Customer Group",
- "customer_name": "_Test Customer",
- "doctype": "Sales Order",
- "base_grand_total": 1000.0,
- "grand_total": 1000.0,
- "naming_series": "_T-Sales Order-",
- "order_type": "Sales",
- "plc_conversion_rate": 1.0,
- "price_list_currency": "INR",
+ "company": "_Test Company",
+ "conversion_rate": 1.0,
+ "currency": "INR",
+ "customer": "_Test Customer",
+ "customer_group": "_Test Customer Group",
+ "customer_name": "_Test Customer",
+ "doctype": "Sales Order",
+ "base_grand_total": 1000.0,
+ "grand_total": 1000.0,
+ "naming_series": "_T-Sales Order-",
+ "order_type": "Sales",
+ "plc_conversion_rate": 1.0,
+ "price_list_currency": "INR",
"items": [
{
- "base_amount": 1000.0,
- "base_rate": 100.0,
- "description": "CPU",
- "doctype": "Sales Order Item",
- "item_code": "_Test Item Home Desktop 100",
- "item_name": "CPU",
- "delivery_date": "2013-02-23",
- "parentfield": "items",
- "qty": 10.0,
- "rate": 100.0,
+ "base_amount": 1000.0,
+ "base_rate": 100.0,
+ "description": "CPU",
+ "doctype": "Sales Order Item",
+ "item_code": "_Test Item",
+ "item_name": "_Test Item 1",
+ "delivery_date": "2013-02-23",
+ "parentfield": "items",
+ "qty": 10.0,
+ "rate": 100.0,
"warehouse": "_Test Warehouse - _TC",
"stock_uom": "_Test UOM",
"conversion_factor": 1.0,
"uom": "_Test UOM"
}
- ],
- "selling_price_list": "_Test Price List",
- "territory": "_Test Territory",
+ ],
+ "selling_price_list": "_Test Price List",
+ "territory": "_Test Territory",
"transaction_date": "2013-02-21"
}
]
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index ec283461ef8..deace33f343 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -13,7 +13,7 @@ from erpnext.controllers.item_variant import (ItemVariantExistsError,
from erpnext.setup.doctype.item_group.item_group import (get_parent_item_groups, invalidate_cache_for)
from frappe import _, msgprint
from frappe.utils import (cint, cstr, flt, formatdate, get_timestamp, getdate,
- now_datetime, random_string, strip)
+ now_datetime, random_string, strip, get_link_to_form)
from frappe.utils.html_utils import clean_html
from frappe.website.doctype.website_slideshow.website_slideshow import \
get_slideshow
@@ -634,6 +634,9 @@ class Item(WebsiteGenerator):
+ ": \n" + ", ".join([self.meta.get_label(fld) for fld in field_list]))
def after_rename(self, old_name, new_name, merge):
+ if merge:
+ self.validate_duplicate_item_in_stock_reconciliation(old_name, new_name)
+
if self.route:
invalidate_cache_for_item(self)
clear_cache(self.route)
@@ -656,6 +659,27 @@ class Item(WebsiteGenerator):
frappe.db.set_value(dt, d.name, "item_wise_tax_detail",
json.dumps(item_wise_tax_detail), update_modified=False)
+ def validate_duplicate_item_in_stock_reconciliation(self, old_name, new_name):
+ records = frappe.db.sql(""" SELECT parent, COUNT(*) as records
+ FROM `tabStock Reconciliation Item`
+ WHERE item_code = %s and docstatus = 1
+ GROUP By item_code, warehouse, parent
+ HAVING records > 1
+ """, new_name, as_dict=1)
+
+ if not records: return
+ document = _("Stock Reconciliation") if len(records) == 1 else _("Stock Reconciliations")
+
+ msg = _("The items {0} and {1} are present in the following {2} :
"
+ .format(frappe.bold(old_name), frappe.bold(new_name), document))
+
+ msg += ', '.join([get_link_to_form("Stock Reconciliation", d.parent) for d in records]) + "
"
+
+ msg += _("Note: To merge the items, create a separate Stock Reconciliation for the old item {0}"
+ .format(frappe.bold(old_name)))
+
+ frappe.throw(_(msg), title=_("Merge not allowed"))
+
def set_last_purchase_rate(self, new_name):
last_purchase_rate = get_last_purchase_details(new_name).get("base_net_rate", 0)
frappe.db.set_value("Item", new_name, "last_purchase_rate", last_purchase_rate)
diff --git a/erpnext/stock/doctype/item/test_records.json b/erpnext/stock/doctype/item/test_records.json
index 6c1a55945c8..9ca887c77e3 100644
--- a/erpnext/stock/doctype/item/test_records.json
+++ b/erpnext/stock/doctype/item/test_records.json
@@ -92,8 +92,7 @@
{
"doctype": "Item Tax",
"parentfield": "taxes",
- "item_tax_template": "_Test Account Excise Duty @ 10",
- "tax_category": ""
+ "item_tax_template": "_Test Account Excise Duty @ 10"
}
],
"stock_uom": "_Test UOM 1"
@@ -371,8 +370,7 @@
{
"doctype": "Item Tax",
"parentfield": "taxes",
- "item_tax_template": "_Test Account Excise Duty @ 10",
- "tax_category": ""
+ "item_tax_template": "_Test Account Excise Duty @ 10"
},
{
"doctype": "Item Tax",
@@ -451,14 +449,13 @@
{
"doctype": "Item Tax",
"parentfield": "taxes",
- "item_tax_template": "_Test Account Excise Duty @ 20",
- "tax_category": ""
+ "item_tax_template": "_Test Account Excise Duty @ 20"
},
{
"doctype": "Item Tax",
"parentfield": "taxes",
- "item_tax_template": "_Test Item Tax Template 1",
- "tax_category": "_Test Tax Category 1"
+ "tax_category": "_Test Tax Category 1",
+ "item_tax_template": "_Test Item Tax Template 1"
}
]
}
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index b113781def3..1ccd8cf31a0 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -19,6 +19,12 @@ frappe.ui.form.on('Material Request', {
frm.set_indicator_formatter('item_code',
function(doc) { return (doc.stock_qty<=doc.ordered_qty) ? "green" : "orange"; });
+ frm.set_query("from_warehouse", "items", function(doc) {
+ return {
+ filters: {'company': doc.company}
+ };
+ });
+
},
onload: function(frm) {
@@ -27,11 +33,24 @@ frappe.ui.form.on('Material Request', {
// set schedule_date
set_schedule_date(frm);
- frm.fields_dict["items"].grid.get_field("warehouse").get_query = function(doc) {
+
+ frm.set_query("warehouse", "items", function(doc) {
return {
filters: {'company': doc.company}
};
- };
+ });
+
+ frm.set_query("set_warehouse", function(doc){
+ return {
+ filters: {'company': doc.company}
+ };
+ });
+
+ frm.set_query("set_from_warehouse", function(doc){
+ return {
+ filters: {'company': doc.company}
+ };
+ });
},
onload_post_render: function(frm) {
diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json
index 2be14c8006a..b9427289449 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.json
+++ b/erpnext/stock/doctype/serial_no/serial_no.json
@@ -1,6 +1,7 @@
{
"actions": [],
"allow_import": 1,
+ "allow_rename": 1,
"autoname": "field:serial_no",
"creation": "2013-05-16 10:59:15",
"description": "Distinct unit of an Item",
@@ -426,7 +427,7 @@
"icon": "fa fa-barcode",
"idx": 1,
"links": [],
- "modified": "2020-06-25 15:53:50.900855",
+ "modified": "2020-07-22 15:53:50.900855",
"modified_by": "Administrator",
"module": "Stock",
"name": "Serial No",
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 2cb43ae2db3..bbdac992b58 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -193,7 +193,7 @@ class SerialNo(StockController):
def after_rename(self, old, new, merge=False):
"""rename serial_no text fields"""
for dt in frappe.db.sql("""select parent from tabDocField
- where fieldname='serial_no' and fieldtype in ('Text', 'Small Text')"""):
+ where fieldname='serial_no' and fieldtype in ('Text', 'Small Text', 'Long Text')"""):
for item in frappe.db.sql("""select name, serial_no from `tab%s`
where serial_no like %s""" % (dt[0], frappe.db.escape('%' + old + '%'))):
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 36f309c1a6e..0dc87767dde 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -257,6 +257,7 @@ class StockReconciliation(StockController):
sl_entries.append(args)
+ qty_after_transaction = 0
for serial_no in serial_nos:
args = self.get_sle_for_items(row, [serial_no])
@@ -270,11 +271,19 @@ class StockReconciliation(StockController):
if previous_sle and row.warehouse != previous_sle.get("warehouse"):
# If serial no exists in different warehouse
+ warehouse = previous_sle.get("warehouse", '') or row.warehouse
+
+ if not qty_after_transaction:
+ qty_after_transaction = get_stock_balance(row.item_code,
+ warehouse, self.posting_date, self.posting_time)
+
+ qty_after_transaction -= 1
+
new_args = args.copy()
new_args.update({
'actual_qty': -1,
- 'qty_after_transaction': cint(previous_sle.get('qty_after_transaction')) - 1,
- 'warehouse': previous_sle.get("warehouse", '') or row.warehouse,
+ 'qty_after_transaction': qty_after_transaction,
+ 'warehouse': warehouse,
'valuation_rate': previous_sle.get("valuation_rate")
})
diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json
index 0606d0bdc64..909d8216308 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.json
+++ b/erpnext/stock/doctype/warehouse/warehouse.json
@@ -1,5 +1,4 @@
{
- "actions": [],
"allow_import": 1,
"allow_rename": 1,
"creation": "2013-03-07 18:50:32",
@@ -10,12 +9,14 @@
"field_order": [
"warehouse_detail",
"warehouse_name",
+ "section_break_3",
+ "warehouse_type",
+ "parent_warehouse",
"is_group",
- "company",
- "disabled",
"column_break_4",
"account",
- "warehouse_type",
+ "company",
+ "disabled",
"address_and_contact",
"address_html",
"column_break_10",
@@ -31,7 +32,6 @@
"state",
"pin",
"tree_details",
- "parent_warehouse",
"lft",
"rgt",
"old_parent"
@@ -91,6 +91,7 @@
"options": "Account"
},
{
+ "depends_on": "eval:!doc.__islocal",
"fieldname": "address_and_contact",
"fieldtype": "Section Break",
"label": "Address and Contact"
@@ -224,13 +225,16 @@
"fieldtype": "Link",
"label": "Warehouse Type",
"options": "Warehouse Type"
+ },
+ {
+ "fieldname": "section_break_3",
+ "fieldtype": "Section Break"
}
],
"icon": "fa fa-building",
"idx": 1,
"is_tree": 1,
- "links": [],
- "modified": "2020-03-18 18:26:00.479541",
+ "modified": "2020-07-22 14:46:37.650475",
"modified_by": "Administrator",
"module": "Stock",
"name": "Warehouse",