Merge branch 'develop' of https://github.com/frappe/erpnext into develop
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 ab811d8..9552e60 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
@@ -90,7 +90,6 @@
"fieldtype": "Column Break"
},
{
- "default": "Customer",
"fieldname": "party_type",
"fieldtype": "Link",
"in_list_view": 1,
@@ -201,7 +200,7 @@
"fieldname": "reference_type",
"fieldtype": "Select",
"label": "Reference Type",
- "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting"
+ "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"
},
{
"fieldname": "reference_name",
@@ -272,7 +271,7 @@
],
"idx": 1,
"istable": 1,
- "modified": "2019-10-02 12:23:21.693443",
+ "modified": "2020-01-13 12:41:33.968025",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",
@@ -281,4 +280,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js
index e2510f6..e1e4314 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.js
+++ b/erpnext/accounts/doctype/payment_request/payment_request.js
@@ -2,6 +2,16 @@
cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway")
cur_frm.add_fetch("payment_gateway_account", "message", "message")
+frappe.ui.form.on("Payment Request", {
+ setup: function(frm) {
+ frm.set_query("party_type", function() {
+ return {
+ query: "erpnext.setup.doctype.party_type.party_type.get_party_type",
+ };
+ });
+ }
+})
+
frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){
if (frm.doc.reference_doctype) {
frappe.call({
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 530bd89..a2a47b3 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -5,7 +5,7 @@
import frappe
import unittest, copy, time
-from frappe.utils import nowdate, flt, getdate, cint
+from frappe.utils import nowdate, flt, getdate, cint, add_days
from frappe.model.dynamic_links import get_dynamic_link_map
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
@@ -1847,6 +1847,26 @@
self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234')
self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000)
+ def test_item_tax_validity(self):
+ item = frappe.get_doc("Item", "_Test Item 2")
+
+ if item.taxes:
+ item.taxes = []
+ item.save()
+
+ item.append("taxes", {
+ "item_tax_template": "_Test Item Tax Template 1",
+ "valid_from": add_days(nowdate(), 1)
+ })
+
+ item.save()
+
+ sales_invoice = create_sales_invoice(item = "_Test Item 2", do_not_save=1)
+ sales_invoice.items[0].item_tax_template = "_Test Item Tax Template 1"
+ self.assertRaises(frappe.ValidationError, sales_invoice.save)
+
+ item.taxes = []
+ item.save()
def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice")
diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js
index 873c65e..0e93035 100644
--- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js
+++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js
@@ -66,6 +66,21 @@
}
},
{
+ "fieldname": "account",
+ "label": __("Account"),
+ "fieldtype": "Link",
+ "options": "Account",
+ "get_query": function() {
+ var company = frappe.query_report.get_filter_value('company');
+ return {
+ "doctype": "Account",
+ "filters": {
+ "company": company,
+ }
+ }
+ }
+ },
+ {
"fieldname": "show_zero_values",
"label": __("Show zero values"),
"fieldtype": "Check"
diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
index 3e47906..78c7e43 100644
--- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
+++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
@@ -20,7 +20,7 @@
def get_data(filters, show_party_name):
if filters.get('party_type') in ('Customer', 'Supplier', 'Employee', 'Member'):
party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type')))
- if filters.get('party_type') == 'Student':
+ elif filters.get('party_type') == 'Student':
party_name_field = 'first_name'
elif filters.get('party_type') == 'Shareholder':
party_name_field = 'title'
@@ -96,13 +96,19 @@
return data
def get_opening_balances(filters):
+
+ account_filter = ''
+ if filters.get('account'):
+ account_filter = "and account = %s" % (frappe.db.escape(filters.get('account')))
+
gle = frappe.db.sql("""
select party, sum(debit) as opening_debit, sum(credit) as opening_credit
from `tabGL Entry`
where company=%(company)s
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
- group by party""", {
+ {account_filter}
+ group by party""".format(account_filter=account_filter), {
"company": filters.company,
"from_date": filters.from_date,
"party_type": filters.party_type
@@ -116,6 +122,11 @@
return opening
def get_balances_within_period(filters):
+
+ account_filter = ''
+ if filters.get('account'):
+ account_filter = "and account = %s" % (frappe.db.escape(filters.get('account')))
+
gle = frappe.db.sql("""
select party, sum(debit) as debit, sum(credit) as credit
from `tabGL Entry`
@@ -123,7 +134,8 @@
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
and posting_date >= %(from_date)s and posting_date <= %(to_date)s
and ifnull(is_opening, 'No') = 'No'
- group by party""", {
+ {account_filter}
+ group by party""".format(account_filter=account_filter), {
"company": filters.company,
"from_date": filters.from_date,
"to_date": filters.to_date,
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 89c8467..e01d6d5 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -891,3 +891,9 @@
def generator():
return cint(frappe.db.get_value('Accounts Settings', None, 'allow_cost_center_in_entry_of_bs_account'))
return frappe.local_cache("get_allow_cost_center_in_entry_of_bs_account", (), generator, regenerate_if_none=True)
+
+def get_stock_accounts(company):
+ return frappe.get_all("Account", filters = {
+ "account_type": "Stock",
+ "company": company
+ })
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index f6a7fa2..a53ff88 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -338,25 +338,12 @@
})
},
- purchase_receipt: function(frm) {
+ purchase_receipt: (frm) => {
frm.trigger('toggle_reference_doc');
-
if (frm.doc.purchase_receipt) {
if (frm.doc.item_code) {
frappe.db.get_doc('Purchase Receipt', frm.doc.purchase_receipt).then(pr_doc => {
- frm.set_value('company', pr_doc.company);
- frm.set_value('purchase_date', pr_doc.posting_date);
- const item = pr_doc.items.find(item => item.item_code === frm.doc.item_code);
- if (!item) {
- frm.set_value('purchase_receipt', '');
- frappe.msgprint({
- title: __('Invalid Purchase Receipt'),
- message: __("The selected Purchase Receipt doesn't contains selected Asset Item."),
- indicator: 'red'
- });
- }
- frm.set_value('gross_purchase_amount', item.base_net_rate);
- frm.set_value('location', item.asset_location);
+ frm.events.set_values_from_purchase_doc(frm, 'Purchase Receipt', pr_doc)
});
} else {
frm.set_value('purchase_receipt', '');
@@ -368,24 +355,12 @@
}
},
- purchase_invoice: function(frm) {
+ purchase_invoice: (frm) => {
frm.trigger('toggle_reference_doc');
if (frm.doc.purchase_invoice) {
if (frm.doc.item_code) {
frappe.db.get_doc('Purchase Invoice', frm.doc.purchase_invoice).then(pi_doc => {
- frm.set_value('company', pi_doc.company);
- frm.set_value('purchase_date', pi_doc.posting_date);
- const item = pi_doc.items.find(item => item.item_code === frm.doc.item_code);
- if (!item) {
- frm.set_value('purchase_invoice', '');
- frappe.msgprint({
- title: __('Invalid Purchase Invoice'),
- message: __("The selected Purchase Invoice doesn't contains selected Asset Item."),
- indicator: 'red'
- });
- }
- frm.set_value('gross_purchase_amount', item.base_net_rate);
- frm.set_value('location', item.asset_location);
+ frm.events.set_values_from_purchase_doc(frm, 'Purchase Invoice', pi_doc)
});
} else {
frm.set_value('purchase_invoice', '');
@@ -397,6 +372,24 @@
}
},
+ 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);
+ const item = purchase_doc.items.find(item => item.item_code === frm.doc.item_code);
+ if (!item) {
+ doctype_field = frappe.scrub(doctype)
+ frm.set_value(doctype_field, '');
+ frappe.msgprint({
+ title: __(`Invalid ${doctype}`),
+ message: __(`The selected ${doctype} doesn't contains selected Asset Item.`),
+ indicator: 'red'
+ });
+ }
+ frm.set_value('gross_purchase_amount', item.base_net_rate + item.item_tax_amount);
+ frm.set_value('purchase_receipt_amount', item.base_net_rate + item.item_tax_amount);
+ frm.set_value('location', item.asset_location);
+ },
+
set_depreciation_rate: function(frm, row) {
if (row.total_number_of_depreciations && row.frequency_of_depreciation
&& row.expected_value_after_useful_life) {
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 3e7f683..86b5a11 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -132,9 +132,10 @@
if len(movements) > 1:
frappe.throw(_('Asset has multiple Asset Movement Entries which has to be \
cancelled manually to cancel this asset.'))
- movement = frappe.get_doc('Asset Movement', movements[0].get('name'))
- movement.flags.ignore_validate = True
- movement.cancel()
+ if movements:
+ movement = frappe.get_doc('Asset Movement', movements[0].get('name'))
+ movement.flags.ignore_validate = True
+ movement.cancel()
def make_asset_movement(self):
reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
diff --git a/erpnext/config/stock.py b/erpnext/config/stock.py
index 03e26b2..dd35f5a 100644
--- a/erpnext/config/stock.py
+++ b/erpnext/config/stock.py
@@ -348,6 +348,12 @@
"is_query_report": True,
"name": "Subcontracted Item To Be Received",
"doctype": "Purchase Order"
+ },
+ {
+ "type": "report",
+ "is_query_report": True,
+ "name": "Stock and Account Value Comparison",
+ "doctype": "Stock Ledger Entry"
}
]
},
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 5c31900..d18f8e5 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -4,9 +4,9 @@
from __future__ import unicode_literals
import frappe
from frappe.desk.reportview import get_match_cond, get_filters_cond
-from frappe.utils import nowdate
+from frappe.utils import nowdate, getdate
from collections import defaultdict
-
+from erpnext.stock.get_item_details import _get_item_tax_template
# searches for active employees
def employee_query(doctype, txt, searchfield, start, page_len, filters):
@@ -486,7 +486,7 @@
@frappe.whitelist()
def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters):
query = """
- select pr.name
+ select pr.name
from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pritem
where pr.docstatus = 1 and pritem.parent = pr.name
and pr.name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt)))
@@ -499,7 +499,7 @@
@frappe.whitelist()
def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters):
query = """
- select pi.name
+ select pi.name
from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` piitem
where pi.docstatus = 1 and piitem.parent = pi.name
and pi.name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt)))
@@ -508,3 +508,27 @@
query += " and piitem.item_code = {item_code}".format(item_code = frappe.db.escape(filters.get('item_code')))
return frappe.db.sql(query, filters)
+
+@frappe.whitelist()
+def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
+
+ item_doc = frappe.get_cached_doc('Item', filters.get('item_code'))
+ item_group = filters.get('item_group')
+ taxes = item_doc.taxes or []
+
+ while item_group:
+ item_group_doc = frappe.get_cached_doc('Item Group', item_group)
+ taxes += item_group_doc.taxes or []
+ item_group = item_group_doc.parent_item_group
+
+ if not taxes:
+ return frappe.db.sql(""" SELECT name FROM `tabItem Tax Template` """)
+ else:
+ args = {
+ 'item_code': filters.get('item_code'),
+ 'posting_date': filters.get('valid_from'),
+ 'tax_category': filters.get('tax_category')
+ }
+
+ taxes = _get_item_tax_template(args, taxes, for_validate=True)
+ return [(d,) for d in set(taxes)]
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 049a837..b52a07d 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -8,6 +8,7 @@
from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
from erpnext.controllers.accounts_controller import validate_conversion_rate, \
validate_taxes_and_charges, validate_inclusive_tax
+from erpnext.stock.get_item_details import _get_item_tax_template
class calculate_taxes_and_totals(object):
def __init__(self, doc):
@@ -34,6 +35,7 @@
def _calculate(self):
self.validate_conversion_rate()
self.calculate_item_values()
+ self.validate_item_tax_template()
self.initialize_taxes()
self.determine_exclusive_rate()
self.calculate_net_total()
@@ -43,6 +45,38 @@
self._cleanup()
self.calculate_total_net_weight()
+ def validate_item_tax_template(self):
+ for item in self.doc.get('items'):
+ if item.item_code and item.get('item_tax_template'):
+ item_doc = frappe.get_cached_doc("Item", item.item_code)
+ args = {
+ 'tax_category': self.doc.get('tax_category'),
+ 'posting_date': self.doc.get('posting_date'),
+ 'bill_date': self.doc.get('bill_date'),
+ 'transaction_date': self.doc.get('transaction_date')
+ }
+
+ item_group = item_doc.item_group
+ item_group_taxes = []
+
+ while item_group:
+ item_group_doc = frappe.get_cached_doc('Item Group', item_group)
+ item_group_taxes += item_group_doc.taxes or []
+ item_group = item_group_doc.parent_item_group
+
+ item_taxes = item_doc.taxes or []
+
+ if not item_group_taxes and (not item_taxes):
+ # No validation if no taxes in item or item group
+ continue
+
+ taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
+
+ if item.item_tax_template not in taxes:
+ frappe.throw(_("Row {0}: Invalid Item Tax Template for item {1}").format(
+ item.idx, frappe.bold(item.item_code)
+ ))
+
def validate_conversion_rate(self):
# validate conversion rate
company_currency = erpnext.get_company_currency(self.doc.company)
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json
index 9903048..df77ad8 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"creation": "2018-10-25 10:02:48.656165",
"doctype": "DocType",
"editable_grid": 1,
@@ -23,48 +24,41 @@
},
{
"default": "0",
- "depends_on": "eval:doc.enabled==1",
+ "depends_on": "enabled",
"fieldname": "automatic_sync",
"fieldtype": "Check",
"label": "Synchronize all accounts every hour"
},
{
- "depends_on": "eval:doc.enabled==1",
"fieldname": "plaid_client_id",
"fieldtype": "Data",
"in_list_view": 1,
- "label": "Plaid Client ID",
- "reqd": 1
+ "label": "Plaid Client ID"
},
{
- "depends_on": "eval:doc.enabled==1",
"fieldname": "plaid_secret",
"fieldtype": "Password",
"in_list_view": 1,
- "label": "Plaid Secret",
- "reqd": 1
+ "label": "Plaid Secret"
},
{
- "depends_on": "eval:doc.enabled==1",
"fieldname": "plaid_public_key",
"fieldtype": "Data",
"in_list_view": 1,
- "label": "Plaid Public Key",
- "reqd": 1
+ "label": "Plaid Public Key"
},
{
- "depends_on": "eval:doc.enabled==1",
"fieldname": "plaid_env",
"fieldtype": "Data",
"in_list_view": 1,
- "label": "Plaid Environment",
- "reqd": 1
+ "label": "Plaid Environment"
},
{
"fieldname": "column_break_2",
"fieldtype": "Column Break"
},
{
+ "depends_on": "enabled",
"fieldname": "section_break_4",
"fieldtype": "Section Break"
},
@@ -74,7 +68,8 @@
}
],
"issingle": 1,
- "modified": "2019-08-13 17:00:06.939422",
+ "links": [],
+ "modified": "2020-01-05 10:00:22.137832",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "Plaid Settings",
diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py
index 7cec362..5a71ca5 100644
--- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py
+++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py
@@ -6,6 +6,7 @@
import frappe, json
from frappe import _
from frappe.model.document import Document
+from frappe.model.rename_doc import rename_doc
from frappe.utils import nowdate
class ClinicalProcedureTemplate(Document):
@@ -116,7 +117,7 @@
"item_code": item_code})):
frappe.throw(_("Code {0} already exist").format(item_code))
else:
- frappe.rename_doc("Item", doc.item_code, item_code, ignore_permissions = True)
+ rename_doc("Item", doc.item_code, item_code, ignore_permissions=True)
frappe.db.set_value("Clinical Procedure Template", doc.name, "item_code", item_code)
return
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py
index 6504994..43f01c8 100644
--- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py
+++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py
@@ -6,6 +6,7 @@
import frappe
from frappe import _
from frappe.model.document import Document
+from frappe.model.rename_doc import rename_doc
class HealthcareServiceUnitType(Document):
def validate(self):
@@ -107,7 +108,7 @@
if(item_exist):
frappe.throw(_("Code {0} already exist").format(item_code))
else:
- frappe.rename_doc("Item", item, item_code, ignore_permissions = True)
+ rename_doc("Item", item, item_code, ignore_permissions=True)
frappe.db.set_value("Healthcare Service Unit Type", doc_name, "item_code", item_code)
@frappe.whitelist()
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py
index 101e143..91488e3 100644
--- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py
+++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py
@@ -5,6 +5,7 @@
from __future__ import unicode_literals
import frappe, json
from frappe.model.document import Document
+from frappe.model.rename_doc import rename_doc
from frappe import _
class LabTestTemplate(Document):
@@ -112,9 +113,9 @@
if(item_exist):
frappe.throw(_("Code {0} already exist").format(lab_test_code))
else:
- frappe.rename_doc("Item", doc.name, lab_test_code, ignore_permissions = True)
+ rename_doc("Item", doc.name, lab_test_code, ignore_permissions=True)
frappe.db.set_value("Lab Test Template",doc.name,"lab_test_code",lab_test_code)
- frappe.rename_doc("Lab Test Template", doc.name, lab_test_code, ignore_permissions = True)
+ rename_doc("Lab Test Template", doc.name, lab_test_code, ignore_permissions=True)
return lab_test_code
@frappe.whitelist()
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js
index 4004c1c..b629b42 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.js
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.js
@@ -2,10 +2,6 @@
// For license information, please see license.txt
frappe.ui.form.on('HR Settings', {
- refresh: function(frm) {
-
- },
-
encrypt_salary_slips_in_emails: function(frm) {
let encrypt_state = frm.doc.encrypt_salary_slips_in_emails;
frm.set_df_property('password_policy', 'reqd', encrypt_state);
@@ -19,5 +15,9 @@
}
frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-'));
}
+ },
+
+ restrict_backdated_leave_application: function(frm) {
+ frm.toggle_reqd("role_allowed_to_create_backdated_leave_application", frm.doc.restrict_backdated_leave_application);
}
-});
+});
\ No newline at end of file
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json
index 6cb0e21..90f4988 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.json
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.json
@@ -23,10 +23,12 @@
"leave_settings",
"leave_approval_notification_template",
"leave_status_notification_template",
+ "role_allowed_to_create_backdated_leave_application",
"column_break_18",
"leave_approver_mandatory_in_leave_application",
"show_leaves_of_all_department_members_in_calendar",
"auto_leave_encashment",
+ "restrict_backdated_leave_application",
"hiring_settings",
"check_vacancies"
],
@@ -169,13 +171,26 @@
"fieldname": "disable_rounded_total",
"fieldtype": "Check",
"label": "Disable Rounded Total"
+ },
+ {
+ "default": "0",
+ "fieldname": "restrict_backdated_leave_application",
+ "fieldtype": "Check",
+ "label": "Restrict Backdated Leave Application"
+ },
+ {
+ "depends_on": "eval:doc.restrict_backdated_leave_application == 1",
+ "fieldname": "role_allowed_to_create_backdated_leave_application",
+ "fieldtype": "Link",
+ "label": "Role Allowed to Create Backdated Leave Application",
+ "options": "Role"
}
],
"icon": "fa fa-cog",
"idx": 1,
"issingle": 1,
"links": [],
- "modified": "2019-12-31 14:28:32.004121",
+ "modified": "2020-01-06 18:46:30.189815",
"modified_by": "Administrator",
"module": "HR",
"name": "HR Settings",
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index 5222712..7594cb7 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -79,6 +79,12 @@
frappe.throw(_("{0} applicable after {1} working days").format(self.leave_type, leave_type.applicable_after))
def validate_dates(self):
+ if frappe.db.get_single_value("HR Settings", "restrict_backdated_leave_application"):
+ if self.from_date and self.from_date < frappe.utils.today():
+ allowed_role = frappe.db.get_single_value("HR Settings", "role_allowed_to_create_backdated_leave_application")
+ if allowed_role not in frappe.get_roles():
+ frappe.throw(_("Only users with the {0} role can create backdated leave applications").format(allowed_role))
+
if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)):
frappe.throw(_("To date cannot be before from date"))
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index cea4662..1989f4d 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -645,7 +645,6 @@
erpnext.patches.v12_0.replace_accounting_with_accounts_in_home_settings
erpnext.patches.v12_0.set_payment_entry_status
erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields
-erpnext.patches.v12_0.set_default_for_add_taxes_from_item_tax_template
erpnext.patches.v12_0.add_export_type_field_in_party_master
erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger
erpnext.patches.v12_0.update_price_or_product_discount
diff --git a/erpnext/patches/v11_0/merge_land_unit_with_location.py b/erpnext/patches/v11_0/merge_land_unit_with_location.py
index 1ea486d..7845da2 100644
--- a/erpnext/patches/v11_0/merge_land_unit_with_location.py
+++ b/erpnext/patches/v11_0/merge_land_unit_with_location.py
@@ -4,19 +4,18 @@
from __future__ import unicode_literals
import frappe
-from frappe.model.rename_doc import rename_doc
from frappe.model.utils.rename_field import rename_field
def execute():
# Rename and reload the Land Unit and Linked Land Unit doctypes
if frappe.db.table_exists('Land Unit') and not frappe.db.table_exists('Location'):
- rename_doc('DocType', 'Land Unit', 'Location', force=True)
+ frappe.rename_doc('DocType', 'Land Unit', 'Location', force=True)
frappe.reload_doc('assets', 'doctype', 'location')
if frappe.db.table_exists('Linked Land Unit') and not frappe.db.table_exists('Linked Location'):
- rename_doc('DocType', 'Linked Land Unit', 'Linked Location', force=True)
+ frappe.rename_doc('DocType', 'Linked Land Unit', 'Linked Location', force=True)
frappe.reload_doc('assets', 'doctype', 'linked_location')
diff --git a/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py b/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py
index c03ab0b..fad0cf7 100644
--- a/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py
+++ b/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py
@@ -3,9 +3,9 @@
from __future__ import unicode_literals
import frappe
-from frappe.model.rename_doc import rename_doc
+
def execute():
if frappe.db.table_exists("Asset Adjustment") and not frappe.db.table_exists("Asset Value Adjustment"):
- rename_doc('DocType', 'Asset Adjustment', 'Asset Value Adjustment', force=True)
+ frappe.rename_doc('DocType', 'Asset Adjustment', 'Asset Value Adjustment', force=True)
frappe.reload_doc('assets', 'doctype', 'asset_value_adjustment')
\ No newline at end of file
diff --git a/erpnext/patches/v11_0/rename_health_insurance.py b/erpnext/patches/v11_0/rename_health_insurance.py
index 24d1ddf..e605071 100644
--- a/erpnext/patches/v11_0/rename_health_insurance.py
+++ b/erpnext/patches/v11_0/rename_health_insurance.py
@@ -2,9 +2,8 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-from frappe.model.rename_doc import rename_doc
import frappe
def execute():
- rename_doc('DocType', 'Health Insurance', 'Employee Health Insurance', force=True)
+ frappe.rename_doc('DocType', 'Health Insurance', 'Employee Health Insurance', force=True)
frappe.reload_doc('hr', 'doctype', 'employee_health_insurance')
\ No newline at end of file
diff --git a/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py b/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py
index 8fdac07..9705681 100644
--- a/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py
+++ b/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
import frappe
-from frappe.model.rename_doc import rename_doc
from frappe.model.utils.rename_field import rename_field
from frappe.modules import scrub, get_doctype_module
@@ -37,7 +36,7 @@
def execute():
for dt in doc_rename_map:
if frappe.db.exists('DocType', dt):
- rename_doc('DocType', dt, doc_rename_map[dt], force=True)
+ frappe.rename_doc('DocType', dt, doc_rename_map[dt], force=True)
for dn in field_rename_map:
if frappe.db.exists('DocType', dn):
diff --git a/erpnext/patches/v11_0/rename_production_order_to_work_order.py b/erpnext/patches/v11_0/rename_production_order_to_work_order.py
index 2c27fbb..2f620f4 100644
--- a/erpnext/patches/v11_0/rename_production_order_to_work_order.py
+++ b/erpnext/patches/v11_0/rename_production_order_to_work_order.py
@@ -2,18 +2,17 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-from frappe.model.rename_doc import rename_doc
-from frappe.model.utils.rename_field import rename_field
import frappe
+from frappe.model.utils.rename_field import rename_field
def execute():
- rename_doc('DocType', 'Production Order', 'Work Order', force=True)
+ frappe.rename_doc('DocType', 'Production Order', 'Work Order', force=True)
frappe.reload_doc('manufacturing', 'doctype', 'work_order')
- rename_doc('DocType', 'Production Order Item', 'Work Order Item', force=True)
+ frappe.rename_doc('DocType', 'Production Order Item', 'Work Order Item', force=True)
frappe.reload_doc('manufacturing', 'doctype', 'work_order_item')
- rename_doc('DocType', 'Production Order Operation', 'Work Order Operation', force=True)
+ frappe.rename_doc('DocType', 'Production Order Operation', 'Work Order Operation', force=True)
frappe.reload_doc('manufacturing', 'doctype', 'work_order_operation')
frappe.reload_doc('projects', 'doctype', 'timesheet')
diff --git a/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py b/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py
index 52d4621..c4b3838 100644
--- a/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py
+++ b/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
import frappe
-from frappe.model.rename_doc import rename_doc
from frappe.model.utils.rename_field import rename_field
from frappe import _
from frappe.utils.nestedset import rebuild_tree
@@ -9,7 +8,7 @@
if frappe.db.table_exists("Supplier Group"):
frappe.reload_doc('setup', 'doctype', 'supplier_group')
elif frappe.db.table_exists("Supplier Type"):
- rename_doc("DocType", "Supplier Type", "Supplier Group", force=True)
+ frappe.rename_doc("DocType", "Supplier Type", "Supplier Group", force=True)
frappe.reload_doc('setup', 'doctype', 'supplier_group')
frappe.reload_doc("accounts", "doctype", "pricing_rule")
frappe.reload_doc("accounts", "doctype", "tax_rule")
diff --git a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
index e47344b..5a12795 100644
--- a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
+++ b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
@@ -60,10 +60,10 @@
'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice',
'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice'
]
-
+
for dt in doctypes:
for d in frappe.db.sql("""select name, parenttype, parent, item_code, item_tax_rate from `tab{0} Item`
- where ifnull(item_tax_rate, '') not in ('', '{{}}')
+ where ifnull(item_tax_rate, '') not in ('', '{{}}')
and item_tax_template is NULL""".format(dt), as_dict=1):
item_tax_map = json.loads(d.item_tax_rate)
item_tax_template_name = get_item_tax_template(item_tax_templates,
@@ -98,12 +98,15 @@
company = get_company(parts[-1], parenttype, parent)
parent_account = frappe.db.get_value("Account",
filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account")
+ if not parent_account:
+ parent_account = frappe.db.get_value("Account",
+ filters={"account_type": "Tax", "root_type": "Liability", "is_group": 1, "company": company})
filters = {
"account_name": account_name,
- "company": company,
- "account_type": "Tax",
- "parent_account": parent_account
- }
+ "company": company,
+ "account_type": "Tax",
+ "parent_account": parent_account
+ }
tax_type = frappe.db.get_value("Account", filters)
if not tax_type:
account = frappe.new_doc("Account")
diff --git a/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py b/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py
index 41ac8cf..b9ad622 100644
--- a/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py
+++ b/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py
@@ -3,7 +3,6 @@
from __future__ import unicode_literals
import frappe
-from frappe.model.rename_doc import rename_doc
doctypes = {
'Price Discount Slab': 'Promotional Scheme Price Discount',
@@ -16,6 +15,6 @@
def execute():
for old_doc, new_doc in doctypes.items():
if not frappe.db.table_exists(new_doc) and frappe.db.table_exists(old_doc):
- rename_doc('DocType', old_doc, new_doc)
+ frappe.rename_doc('DocType', old_doc, new_doc)
frappe.reload_doc("accounts", "doctype", frappe.scrub(new_doc))
frappe.delete_doc("DocType", old_doc)
diff --git a/erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py b/erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py
deleted file mode 100644
index 06ee798..0000000
--- a/erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import frappe
-
-def execute():
- frappe.db.set_value("Accounts Settings", None, "add_taxes_from_item_tax_template", 1)
- frappe.db.set_default("add_taxes_from_item_tax_template", 1)
\ No newline at end of file
diff --git a/erpnext/patches/v9_2/rename_translated_domains_in_en.py b/erpnext/patches/v9_2/rename_translated_domains_in_en.py
index aec5d43..e5a9e24 100644
--- a/erpnext/patches/v9_2/rename_translated_domains_in_en.py
+++ b/erpnext/patches/v9_2/rename_translated_domains_in_en.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
+from frappe.model.rename_doc import rename_doc
def execute():
frappe.reload_doc('stock', 'doctype', 'item')
@@ -20,11 +21,11 @@
if frappe.db.exists("Domain", domain):
merge=True
- frappe.rename_doc("Domain", translated_domain, domain, ignore_permissions=True, merge=merge)
+ rename_doc("Domain", translated_domain, domain, ignore_permissions=True, merge=merge)
domain_settings = frappe.get_single("Domain Settings")
active_domains = [d.domain for d in domain_settings.active_domains]
-
+
try:
for domain in active_domains:
domain = frappe.get_doc("Domain", domain)
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index 926227b..3d4c4a6 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -107,6 +107,12 @@
filters:{ 'item_code': row.item_code }
}
});
+
+ if(this.frm.fields_dict["items"].grid.get_field('item_code')) {
+ this.frm.set_query("item_tax_template", "items", function(doc, cdt, cdn) {
+ return me.set_query_for_item_tax_template(doc, cdt, cdn)
+ });
+ }
},
refresh: function(doc) {
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 748e623..8cfde8c 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -968,7 +968,7 @@
qty: function(doc, cdt, cdn) {
let item = frappe.get_doc(cdt, cdn);
- this.conversion_factor(doc, cdt, cdn, true);
+ this.conversion_factor(doc, cdt, cdn, false);
this.apply_pricing_rule(item, true);
},
@@ -1700,6 +1700,29 @@
}
},
+ set_query_for_item_tax_template: function(doc, cdt, cdn) {
+
+ var item = frappe.get_doc(cdt, cdn);
+ if(!item.item_code) {
+ frappe.throw(__("Please enter Item Code to get item taxes"));
+ } else {
+
+ let filters = {
+ 'item_code': item.item_code,
+ 'valid_from': doc.transaction_date || doc.bill_date || doc.posting_date,
+ 'item_group': item.item_group,
+ }
+
+ if (doc.tax_category)
+ filters['tax_category'] = doc.tax_category;
+
+ return {
+ query: "erpnext.controllers.queries.get_tax_template",
+ filters: filters
+ }
+ }
+ },
+
payment_terms_template: function() {
var me = this;
const doc = this.frm.doc;
diff --git a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py
index fa2cb12..86cd4d1 100644
--- a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py
+++ b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py
@@ -25,5 +25,9 @@
item_to_be_updated.taxes = []
for tax in taxes:
tax = frappe._dict(tax)
- item_to_be_updated.append("taxes", {'item_tax_template': tax.item_tax_template, 'tax_category': tax.tax_category})
+ item_to_be_updated.append("taxes", {
+ 'item_tax_template': tax.item_tax_template,
+ 'tax_category': tax.tax_category,
+ 'valid_from': tax.valid_from
+ })
item_to_be_updated.save()
\ No newline at end of file
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index cb016a1..fa765df 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -6,7 +6,7 @@
frappe.ui.form.on("Sales Order", {
setup: function(frm) {
frm.custom_make_buttons = {
- 'Delivery Note': 'Delivery',
+ 'Delivery Note': 'Delivery Note',
'Pick List': 'Pick List',
'Sales Invoice': 'Invoice',
'Material Request': 'Material Request',
@@ -135,7 +135,6 @@
}
if(doc.status !== 'Closed') {
if(doc.status !== 'On Hold') {
-
allow_delivery = this.frm.doc.items.some(item => item.delivered_by_supplier === 0 && item.qty > flt(item.delivered_qty))
&& !this.frm.doc.skip_delivery_note
@@ -697,4 +696,4 @@
});
}
});
-$.extend(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm}));
\ No newline at end of file
+$.extend(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm}));
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 3462002..e7cbf40 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -376,6 +376,9 @@
def get_work_order_items(self, for_raw_material_request=0):
'''Returns items with BOM that already do not have a linked work order'''
items = []
+ item_codes = [i.item_code for i in self.items]
+ product_bundle_parents = [pb.new_item_code for pb in frappe.get_all("Product Bundle", {"new_item_code": ["in", item_codes]}, ["new_item_code"])]
+
for table in [self.items, self.packed_items]:
for i in table:
bom = get_default_bom_item(i.item_code)
@@ -387,7 +390,7 @@
else:
pending_qty = stock_qty
- if pending_qty:
+ if pending_qty and i.item_code not in product_bundle_parents:
if bom:
items.append(dict(
name= i.name,
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index 1c9b30b..8278745 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -84,6 +84,13 @@
return me.set_query_for_batch(doc, cdt, cdn)
});
}
+
+ if(this.frm.fields_dict["items"].grid.get_field('item_code')) {
+ this.frm.set_query("item_tax_template", "items", function(doc, cdt, cdn) {
+ return me.set_query_for_item_tax_template(doc, cdt, cdn)
+ });
+ }
+
},
refresh: function() {
diff --git a/erpnext/setup/doctype/authorization_control/authorization_control.py b/erpnext/setup/doctype/authorization_control/authorization_control.py
index 7db703f..0c52b83 100644
--- a/erpnext/setup/doctype/authorization_control/authorization_control.py
+++ b/erpnext/setup/doctype/authorization_control/authorization_control.py
@@ -76,7 +76,7 @@
add_cond = ''
auth_value = av_dis
- if val == 1: add_cond += " and system_user = '"+session['user'].replace("'", "\\'")+"'"
+ if val == 1: add_cond += " and system_user = '"+ frappe.db.escape(session['user'])+"'"
elif val == 2: add_cond += " and system_role IN %s" % ("('"+"','".join(frappe.get_roles())+"')")
else: add_cond += " and ifnull(system_user,'') = '' and ifnull(system_role,'') = ''"
@@ -85,7 +85,7 @@
if doc_obj:
if doc_obj.doctype == 'Sales Invoice': customer = doc_obj.customer
else: customer = doc_obj.customer_name
- add_cond = " and master_name = '"+cstr(customer).replace("'", "\\'")+"'"
+ add_cond = " and master_name = '"+ frappe.db.escape(customer) +"'"
if based_on == 'Itemwise Discount':
if doc_obj:
for t in doc_obj.get("items"):
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index e3d356f..dd3248b 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -49,7 +49,7 @@
if (!frm.doc.is_fixed_asset) {
erpnext.item.make_dashboard(frm);
}
-
+
if (frm.doc.is_fixed_asset) {
frm.trigger('is_fixed_asset');
frm.trigger('auto_create_assets');
@@ -136,14 +136,14 @@
frm.toggle_reqd('customer', frm.doc.is_customer_provided_item ? 1:0);
},
- gst_hsn_code: function(frm){
- if(!frm.doc.taxes){
- frappe.db.get_doc("GST HSN Code", frm.doc.gst_hsn_code).then(hsn_doc=>{
- frm.doc.taxes = [];
+ gst_hsn_code: function(frm) {
+ if(!frm.doc.taxes || !frm.doc.taxes.length) {
+ frappe.db.get_doc("GST HSN Code", frm.doc.gst_hsn_code).then(hsn_doc => {
$.each(hsn_doc.taxes || [], function(i, tax) {
let a = frappe.model.add_child(cur_frm.doc, 'Item Tax', 'taxes');
a.item_tax_template = tax.item_tax_template;
a.tax_category = tax.tax_category;
+ a.valid_from = tax.valid_from;
frm.refresh_field('taxes');
});
});
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index da53d8d..cbd5e33 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -11,7 +11,6 @@
InvalidItemAttributeValueError, get_variant)
from erpnext.stock.doctype.item.item import StockExistsForTemplate, InvalidBarcode
from erpnext.stock.doctype.item.item import get_uom_conv_factor
-from frappe.model.rename_doc import rename_doc
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.stock.get_item_details import get_item_details
@@ -348,7 +347,7 @@
make_stock_entry(item_code="Test Item for Merging 2", target="_Test Warehouse 1 - _TC",
qty=1, rate=100)
- rename_doc("Item", "Test Item for Merging 1", "Test Item for Merging 2", merge=True)
+ frappe.rename_doc("Item", "Test Item for Merging 1", "Test Item for Merging 2", merge=True)
self.assertFalse(frappe.db.exists("Item", "Test Item for Merging 1"))
diff --git a/erpnext/stock/doctype/item_tax/item_tax.json b/erpnext/stock/doctype/item_tax/item_tax.json
index 37daa29..a93e463 100644
--- a/erpnext/stock/doctype/item_tax/item_tax.json
+++ b/erpnext/stock/doctype/item_tax/item_tax.json
@@ -1,107 +1,50 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2013-02-22 01:28:01",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "editable_grid": 1,
+ "actions": [],
+ "creation": "2013-02-22 01:28:01",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "item_tax_template",
+ "tax_category",
+ "valid_from"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "item_tax_template",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Item Tax Template",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "tax_type",
- "oldfieldtype": "Link",
- "options": "Item Tax Template",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "item_tax_template",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Item Tax Template",
+ "oldfieldname": "tax_type",
+ "oldfieldtype": "Link",
+ "options": "Item Tax Template",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_from": "",
- "fieldname": "tax_category",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Tax Category",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "tax_rate",
- "oldfieldtype": "Currency",
- "options": "Tax Category",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldname": "tax_category",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Tax Category",
+ "oldfieldname": "tax_rate",
+ "oldfieldtype": "Currency",
+ "options": "Tax Category"
+ },
+ {
+ "fieldname": "valid_from",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Valid From"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-12-21 23:52:40.798944",
- "modified_by": "Administrator",
- "module": "Stock",
- "name": "Item Tax",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ ],
+ "idx": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2019-12-28 21:54:40.807849",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Item Tax",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC"
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 99195c3..935d613 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -230,7 +230,8 @@
make_purchase_order: function(frm) {
frappe.prompt(
- {fieldname:'default_supplier', label: __('For Default Supplier (optional)'), fieldtype: 'Link', options: 'Supplier'},
+ {fieldname:'default_supplier', label: __('For Default Supplier (optional)'), description: __('Selected Supplier\
+ must be the Default Supplier of one of the items below.'), fieldtype: 'Link', options: 'Supplier'},
(values) => {
frappe.model.open_mapped_doc({
method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order",
@@ -238,7 +239,8 @@
args: { default_supplier: values.default_supplier },
run_link_triggers: true
});
- }
+ },
+ __('Enter Supplier')
)
},
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 01d54de..1c9d4c0 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -103,9 +103,8 @@
if self.work_order and self.purpose == "Material Consumption for Manufacture":
self.validate_work_order_status()
- else:
- self.update_work_order()
+ self.update_work_order()
self.update_stock_ledger()
self.make_gl_entries_on_cancel()
self.update_cost_in_project()
@@ -479,10 +478,16 @@
def set_basic_rate_for_finished_goods(self, raw_material_cost, scrap_material_cost):
if self.purpose in ["Manufacture", "Repack"]:
for d in self.get("items"):
- if d.transfer_qty and (d.bom_no or d.t_warehouse) and (getattr(self, "pro_doc", frappe._dict()).scrap_warehouse != d.t_warehouse):
+ if (d.transfer_qty and (d.bom_no or d.t_warehouse) and raw_material_cost
+ and (getattr(self, "pro_doc", frappe._dict()).scrap_warehouse != d.t_warehouse)):
d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate"))
d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount"))
+ if (not d.basic_rate and self.work_order and
+ frappe.db.get_single_value("Manufacturing Settings", "material_consumption")):
+ d.basic_rate = get_valuation_rate_for_finished_good_entry(self.work_order) or 0
+ d.basic_amount = d.basic_rate * d.qty
+
def distribute_additional_costs(self):
if self.purpose == "Material Issue":
self.additional_costs = []
@@ -833,7 +838,6 @@
(self.purpose == "Manufacture" or self.purpose == "Material Consumption for Manufacture")
and frappe.db.get_single_value("Manufacturing Settings", "material_consumption")== 1):
self.get_unconsumed_raw_materials()
-
else:
if not self.fg_completed_qty:
frappe.throw(_("Manufacturing Quantity is mandatory"))
@@ -1152,20 +1156,17 @@
se_child.s_warehouse = item_dict[d].get("from_warehouse")
se_child.t_warehouse = item_dict[d].get("to_warehouse")
se_child.item_code = item_dict[d].get('item_code') or cstr(d)
- se_child.item_name = item_dict[d]["item_name"]
- se_child.description = item_dict[d]["description"]
se_child.uom = item_dict[d]["uom"] if item_dict[d].get("uom") else stock_uom
se_child.stock_uom = stock_uom
se_child.qty = flt(item_dict[d]["qty"], se_child.precision("qty"))
- se_child.expense_account = item_dict[d].get("expense_account")
se_child.cost_center = item_dict[d].get("cost_center") or cost_center
se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0)
se_child.subcontracted_item = item_dict[d].get("main_item_code")
- se_child.original_item = item_dict[d].get("original_item")
- se_child.po_detail = item_dict[d].get("po_detail")
- if item_dict[d].get("idx"):
- se_child.idx = item_dict[d].get("idx")
+ for field in ["idx", "po_detail", "original_item",
+ "expense_account", "description", "item_name"]:
+ if item_dict[d].get(field):
+ se_child.set(field, item_dict[d].get(field))
if se_child.s_warehouse==None:
se_child.s_warehouse = self.from_warehouse
@@ -1470,6 +1471,24 @@
return used_alternative_items
+def get_valuation_rate_for_finished_good_entry(work_order):
+ work_order_qty = flt(frappe.get_cached_value("Work Order",
+ work_order, 'material_transferred_for_manufacturing'))
+
+ field = "(SUM(total_outgoing_value) / %s) as valuation_rate" % (work_order_qty)
+
+ stock_data = frappe.get_all("Stock Entry",
+ fields = field,
+ filters = {
+ "docstatus": 1,
+ "purpose": "Material Transfer for Manufacture",
+ "work_order": work_order
+ }
+ )
+
+ if stock_data:
+ return stock_data[0].valuation_rate
+
@frappe.whitelist()
def get_uom_details(item_code, uom, qty):
"""Returns dict `{"conversion_factor": [value], "transfer_qty": qty * [value]}`
diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.py b/erpnext/stock/doctype/warehouse/test_warehouse.py
index 121222d..3101e8a 100644
--- a/erpnext/stock/doctype/warehouse/test_warehouse.py
+++ b/erpnext/stock/doctype/warehouse/test_warehouse.py
@@ -1,18 +1,22 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-from frappe.model.rename_doc import rename_doc
-from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+
+import unittest
+
+import frappe
from frappe.utils import cint
-from erpnext import set_perpetual_inventory
from frappe.test_runner import make_test_records
-from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
import erpnext
-import frappe
-import unittest
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext import set_perpetual_inventory
+from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
+
+
test_records = frappe.get_test_records('Warehouse')
+
class TestWarehouse(unittest.TestCase):
def setUp(self):
if not frappe.get_value('Item', '_Test Item'):
@@ -41,7 +45,7 @@
# Rename with abbr
if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 2 - _TC"):
frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC")
- rename_doc("Warehouse", "Test Warehouse for Renaming 1 - _TC", "Test Warehouse for Renaming 2 - _TC")
+ frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 1 - _TC", "Test Warehouse for Renaming 2 - _TC")
self.assertTrue(frappe.db.get_value("Warehouse",
filters={"account": "Test Warehouse for Renaming 1 - _TC"}))
@@ -50,7 +54,7 @@
if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 3 - _TC"):
frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC")
- rename_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC", "Test Warehouse for Renaming 3")
+ frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC", "Test Warehouse for Renaming 3")
self.assertTrue(frappe.db.get_value("Warehouse",
filters={"account": "Test Warehouse for Renaming 1 - _TC"}))
@@ -58,7 +62,7 @@
# Another rename with multiple dashes
if frappe.db.exists("Warehouse", "Test - Warehouse - Company - _TC"):
frappe.delete_doc("Warehouse", "Test - Warehouse - Company - _TC")
- rename_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC", "Test - Warehouse - Company")
+ frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC", "Test - Warehouse - Company")
def test_warehouse_merging(self):
set_perpetual_inventory(1)
@@ -78,7 +82,7 @@
{"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty"))
)
- rename_doc("Warehouse", "Test Warehouse for Merging 1 - _TC",
+ frappe.rename_doc("Warehouse", "Test Warehouse for Merging 1 - _TC",
"Test Warehouse for Merging 2 - _TC", merge=True)
self.assertFalse(frappe.db.exists("Warehouse", "Test Warehouse for Merging 1 - _TC"))
diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py
index 6cdb56b..6ed6044 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.py
+++ b/erpnext/stock/doctype/warehouse/warehouse.py
@@ -177,7 +177,26 @@
return frappe.get_doc("Warehouse", args.docname).convert_to_group_or_ledger()
def get_child_warehouses(warehouse):
- p_warehouse = frappe.get_doc("Warehouse", warehouse)
+ lft, rgt = frappe.get_cached_value("Warehouse", warehouse, [lft, rgt])
return frappe.db.sql_list("""select name from `tabWarehouse`
- where lft >= %s and rgt =< %s""", (p_warehouse.lft, p_warehouse.rgt))
+ where lft >= %s and rgt =< %s""", (lft, rgt))
+
+def get_warehouses_based_on_account(account, company=None):
+ warehouses = []
+ for d in frappe.get_all("Warehouse", fields = ["name", "is_group"],
+ filters = {"account": account}):
+ if d.is_group:
+ warehouses.extend(get_child_warehouses(d.name))
+ else:
+ warehouses.append(d.name)
+
+ if (not warehouses and company and
+ frappe.get_cached_value("Company", company, "default_inventory_account") == account):
+ warehouses = [d.name for d in frappe.get_all("Warehouse", filters={'is_group': 0})]
+
+ if not warehouses:
+ frappe.throw(_("Warehouse not found against the account {0}")
+ .format(account))
+
+ return warehouses
\ No newline at end of file
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 76644ed..b80f99d 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _, throw
-from frappe.utils import flt, cint, add_days, cstr, add_months
+from frappe.utils import flt, cint, add_days, cstr, add_months, getdate
import json, copy
from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item, set_transaction_type
from erpnext.setup.utils import get_exchange_rate
@@ -52,6 +52,16 @@
out = get_basic_details(args, item, overwrite_warehouse)
+ if isinstance(doc, string_types):
+ doc = json.loads(doc)
+
+ if doc and doc.get('doctype') == 'Purchase Invoice':
+ args['bill_date'] = doc.get('bill_date')
+
+ if doc:
+ args['posting_date'] = doc.get('posting_date')
+ args['transaction_date'] = doc.get('transaction_date')
+
get_item_tax_template(args, item, out)
out["item_tax_rate"] = get_item_tax_map(args.company, args.get("item_tax_template") if out.get("item_tax_template") is None \
else out.get("item_tax_template"), as_json=True)
@@ -395,7 +405,34 @@
item_tax_template = _get_item_tax_template(args, item_group_doc.taxes, out)
item_group = item_group_doc.parent_item_group
-def _get_item_tax_template(args, taxes, out):
+def _get_item_tax_template(args, taxes, out={}, for_validate=False):
+ taxes_with_validity = []
+ taxes_with_no_validity = []
+
+ for tax in taxes:
+ if tax.valid_from:
+ # In purchase Invoice first preference will be given to supplier invoice date
+ # if supplier date is not present then posting date
+ validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date')
+
+ if getdate(tax.valid_from) <= getdate(validation_date):
+ taxes_with_validity.append(tax)
+ else:
+ taxes_with_no_validity.append(tax)
+
+ if taxes_with_validity:
+ taxes = sorted(taxes_with_validity, key = lambda i: i.valid_from, reverse=True)
+ else:
+ taxes = taxes_with_no_validity
+
+ if for_validate:
+ return [tax.item_tax_template for tax in taxes if (cstr(tax.tax_category) == cstr(args.get('tax_category')) \
+ and (tax.item_tax_template not in taxes))]
+
+ # all templates have validity and no template is valid
+ if not taxes_with_validity and (not taxes_with_no_validity):
+ return None
+
for tax in taxes:
if cstr(tax.tax_category) == cstr(args.get("tax_category")):
out["item_tax_template"] = tax.item_tax_template
@@ -573,7 +610,7 @@
return frappe.db.sql(""" select name, price_list_rate, uom
from `tabItem Price` {conditions}
- order by uom desc, min_qty desc """.format(conditions=conditions), args)
+ order by uom desc, min_qty desc, valid_from desc """.format(conditions=conditions), args)
def get_price_list_rate_for(args, item_code):
"""
@@ -606,10 +643,15 @@
if desired_qty and check_packing_list(price_list_rate[0][0], desired_qty, item_code):
item_price_data = price_list_rate
else:
- for field in ["customer", "supplier", "min_qty"]:
+ for field in ["customer", "supplier"]:
del item_price_args[field]
general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party"))
+
+ if not general_price_list_rate:
+ del item_price_args["min_qty"]
+ general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party"))
+
if not general_price_list_rate and args.get("uom") != args.get("stock_uom"):
item_price_args["uom"] = args.get("stock_uom")
general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party"))
diff --git a/erpnext/stock/report/stock_and_account_value_comparison/__init__.py b/erpnext/stock/report/stock_and_account_value_comparison/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/report/stock_and_account_value_comparison/__init__.py
diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js
new file mode 100644
index 0000000..7a170be
--- /dev/null
+++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js
@@ -0,0 +1,37 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Stock and Account Value Comparison"] = {
+ "filters": [
+ {
+ "label": __("Company"),
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "options": "Company",
+ "reqd": 1,
+ "default": frappe.defaults.get_user_default("Company")
+ },
+ {
+ "label": __("Account"),
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "options": "Account",
+ get_query: function() {
+ var company = frappe.query_report.get_filter_value('company');
+ return {
+ filters: {
+ "account_type": "Stock",
+ "company": company
+ }
+ }
+ }
+ },
+ {
+ "label": __("As On Date"),
+ "fieldname": "as_on_date",
+ "fieldtype": "Date",
+ "default": frappe.datetime.get_today(),
+ },
+ ]
+};
diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.json b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.json
new file mode 100644
index 0000000..021159a
--- /dev/null
+++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.json
@@ -0,0 +1,28 @@
+{
+ "add_total_row": 1,
+ "creation": "2020-01-09 14:42:45.254751",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "letter_head": "",
+ "modified": "2020-01-09 14:42:45.254751",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Stock and Account Value Comparison",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Stock Ledger Entry",
+ "report_name": "Stock and Account Value Comparison",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Stock User"
+ },
+ {
+ "role": "Accounts Manager"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
new file mode 100644
index 0000000..eef121e
--- /dev/null
+++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
@@ -0,0 +1,131 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe, erpnext
+from frappe import _
+from erpnext.accounts.utils import get_stock_accounts
+from erpnext.accounts.utils import get_currency_precision
+from erpnext.stock.doctype.warehouse.warehouse import get_warehouses_based_on_account
+
+def execute(filters=None):
+ if not erpnext.is_perpetual_inventory_enabled(filters.company):
+ frappe.throw(_("Perpetual inventory required for the company {0} to view this report.")
+ .format(filters.company))
+
+ data = get_data(filters)
+ columns = get_columns(filters)
+
+ return columns, data
+
+def get_data(report_filters):
+ data = []
+
+ filters = {
+ "company": report_filters.company,
+ "posting_date": ("<=", report_filters.as_on_date)
+ }
+
+ currency_precision = get_currency_precision() or 2
+ stock_ledger_entries = get_stock_ledger_data(report_filters, filters)
+ voucher_wise_gl_data = get_gl_data(report_filters, filters)
+
+ for d in stock_ledger_entries:
+ key = (d.voucher_type, d.voucher_no)
+ gl_data = voucher_wise_gl_data.get(key) or {}
+ d.account_value = gl_data.get("account_value", 0)
+ d.difference_value = (d.stock_value - d.account_value)
+ if abs(d.difference_value) > 1.0/10 ** currency_precision:
+ data.append(d)
+
+ return data
+
+def get_stock_ledger_data(report_filters, filters):
+ if report_filters.account:
+ warehouses = get_warehouses_based_on_account(report_filters.account,
+ report_filters.warehouse)
+
+ filters["warehouse"] = ("in", warehouses)
+
+ return frappe.get_all("Stock Ledger Entry", filters=filters,
+ fields = ["name", "voucher_type", "voucher_no",
+ "sum(stock_value_difference) as stock_value", "posting_date", "posting_time"],
+ group_by = "voucher_type, voucher_no",
+ order_by = "posting_date ASC, posting_time ASC")
+
+def get_gl_data(report_filters, filters):
+ if report_filters.account:
+ stock_accounts = [report_filters.account]
+ else:
+ stock_accounts = [k.name
+ for k in get_stock_accounts(report_filters.company)]
+
+ filters.update({
+ "account": ("in", stock_accounts)
+ })
+
+ if filters.get("warehouse"):
+ del filters["warehouse"]
+
+ gl_entries = frappe.get_all("GL Entry", filters=filters,
+ fields = ["name", "voucher_type", "voucher_no",
+ "sum(debit_in_account_currency) - sum(credit_in_account_currency) as account_value"],
+ group_by = "voucher_type, voucher_no")
+
+ voucher_wise_gl_data = {}
+ for d in gl_entries:
+ key = (d.voucher_type, d.voucher_no)
+ voucher_wise_gl_data[key] = d
+
+ return voucher_wise_gl_data
+
+def get_columns(filters):
+ return [
+ {
+ "label": _("Stock Ledger ID"),
+ "fieldname": "name",
+ "fieldtype": "Link",
+ "options": "Stock Ledger Entry",
+ "width": "80"
+ },
+ {
+ "label": _("Posting Date"),
+ "fieldname": "posting_date",
+ "fieldtype": "Date"
+ },
+ {
+ "label": _("Posting Time"),
+ "fieldname": "posting_time",
+ "fieldtype": "Time"
+ },
+ {
+ "label": _("Voucher Type"),
+ "fieldname": "voucher_type",
+ "width": "110"
+ },
+ {
+ "label": _("Voucher No"),
+ "fieldname": "voucher_no",
+ "fieldtype": "Dynamic Link",
+ "options": "voucher_type",
+ "width": "110"
+ },
+ {
+ "label": _("Stock Value"),
+ "fieldname": "stock_value",
+ "fieldtype": "Currency",
+ "width": "120"
+ },
+ {
+ "label": _("Account Value"),
+ "fieldname": "account_value",
+ "fieldtype": "Currency",
+ "width": "120"
+ },
+ {
+ "label": _("Difference Value"),
+ "fieldname": "difference_value",
+ "fieldtype": "Currency",
+ "width": "120"
+ }
+ ]
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index ccba8b0..ff03381 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -264,7 +264,7 @@
`tabItem` item
%s
where
- item.name in (%s) and ifnull(item.disabled, 0) = 0
+ item.name in (%s)
""" % (cf_field, cf_join, ','.join(['%s'] *len(items))), items, as_dict=1)
for item in res:
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 69a4b94..b100f45 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -212,7 +212,7 @@
def get_serialized_values(self, sle):
incoming_rate = flt(sle.incoming_rate)
actual_qty = flt(sle.actual_qty)
- serial_no = cstr(sle.serial_no).split("\n")
+ serial_nos = cstr(sle.serial_no).split("\n")
if incoming_rate < 0:
# wrong incoming rate
@@ -224,9 +224,8 @@
elif actual_qty < 0:
# In case of delivery/stock issue, get average purchase rate
# of serial nos of current entry
- stock_value_change = -1 * flt(frappe.get_all("Serial No",
- fields=["sum(purchase_rate)"],
- filters = {'name': ('in', serial_no)}, as_list=1)[0][0])
+ outgoing_value = self.get_incoming_value_for_serial_nos(sle, serial_nos)
+ stock_value_change = -1 * outgoing_value
new_stock_qty = self.qty_after_transaction + actual_qty
@@ -244,6 +243,36 @@
sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
currency=erpnext.get_company_currency(sle.company))
+ def get_incoming_value_for_serial_nos(self, sle, serial_nos):
+ # get rate from serial nos within same company
+ all_serial_nos = frappe.get_all("Serial No",
+ fields=["purchase_rate", "name", "company"],
+ filters = {'name': ('in', serial_nos)})
+
+ incoming_values = sum([flt(d.purchase_rate) for d in all_serial_nos if d.company==sle.company])
+
+ # Get rate for serial nos which has been transferred to other company
+ invalid_serial_nos = [d.name for d in all_serial_nos if d.company!=sle.company]
+ for serial_no in invalid_serial_nos:
+ incoming_rate = frappe.db.sql("""
+ select incoming_rate
+ from `tabStock Ledger Entry`
+ where
+ company = %s
+ and actual_qty > 0
+ and (serial_no = %s
+ or serial_no like %s
+ or serial_no like %s
+ or serial_no like %s
+ )
+ order by posting_date desc
+ limit 1
+ """, (sle.company, serial_no, serial_no+'\n%', '%\n'+serial_no, '%\n'+serial_no+'\n%'))
+
+ incoming_values += flt(incoming_rate[0][0]) if incoming_rate else 0
+
+ return incoming_values
+
def get_moving_average_values(self, sle):
actual_qty = flt(sle.actual_qty)
new_stock_qty = flt(self.qty_after_transaction) + actual_qty
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index cdff3ff..954f6db 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -304,7 +304,7 @@
DocType: BOM,Total Cost,Gesamtkosten
apps/erpnext/erpnext/hr/doctype/leave_allocation/leave_allocation.js,Allocation Expired!,Zuteilung abgelaufen!
DocType: Soil Analysis,Ca/K,Ca / K
-DocType: Leave Type,Maximum Carry Forwarded Leaves,Maximale Anzahl weitergeleiteter Blätter
+DocType: Leave Type,Maximum Carry Forwarded Leaves,Obergrenze für übertragbaren Urlaub erreicht
DocType: Salary Slip,Employee Loan,MItarbeiterdarlehen
DocType: Additional Salary,HR-ADS-.YY.-.MM.-,HR-ADS-.YY .-. MM.-
DocType: Fee Schedule,Send Payment Request Email,Zahlungaufforderung per E-Mail versenden
@@ -893,7 +893,7 @@
DocType: Vital Signs,Blood Pressure (systolic),Blutdruck (systolisch)
apps/erpnext/erpnext/controllers/buying_controller.py,{0} {1} is {2},{0} {1} ist {2}
DocType: Item Price,Valid Upto,Gültig bis
-DocType: Leave Type,Expire Carry Forwarded Leaves (Days),Verfallsdatum für weitergeleitete Blätter (Tage)
+DocType: Leave Type,Expire Carry Forwarded Leaves (Days),Verfallsdatum für übertragenen Urlaub (Tage)
DocType: Training Event,Workshop,Werkstatt
DocType: Supplier Scorecard Scoring Standing,Warn Purchase Orders,Warnung Bestellungen
apps/erpnext/erpnext/utilities/user_progress.py,List a few of your customers. They could be organizations or individuals.,Bitte ein paar Kunden angeben. Dies können Firmen oder Einzelpersonen sein.
@@ -1103,7 +1103,7 @@
DocType: Payroll Entry,Select Payment Account to make Bank Entry,Wählen Sie ein Zahlungskonto für die Buchung
apps/erpnext/erpnext/config/accounting.py,Opening and Closing,Öffnen und Schließen
DocType: Hotel Settings,Default Invoice Naming Series,Standard-Rechnungsnummernkreis
-apps/erpnext/erpnext/utilities/activation.py,"Create Employee records to manage leaves, expense claims and payroll","Erstellen Sie Mitarbeiterdaten Blätter, Spesenabrechnung und Gehaltsabrechnung zu verwalten"
+apps/erpnext/erpnext/utilities/activation.py,"Create Employee records to manage leaves, expense claims and payroll","Erstellen Sie Mitarbeiterdaten für Urlaubs, Spesenabrechnung und Gehaltsabrechnung zu verwalten"
apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js,An error occurred during the update process,Während des Aktualisierungsprozesses ist ein Fehler aufgetreten
DocType: Restaurant Reservation,Restaurant Reservation,Restaurant Reservierung
apps/erpnext/erpnext/public/js/hub/Sidebar.vue,Your Items,Ihre Artikel
@@ -1548,7 +1548,7 @@
apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py,{0} against Bill {1} dated {2},{0} zu Rechnung {1} vom {2}
apps/erpnext/erpnext/regional/report/eway_bill/eway_bill.py,From State,Aus dem Staat
apps/erpnext/erpnext/utilities/user_progress.py,Setup Institution,Einrichtung Einrichtung
-apps/erpnext/erpnext/hr/doctype/leave_period/leave_period.py,Allocating leaves...,Blätter zuordnen...
+apps/erpnext/erpnext/hr/doctype/leave_period/leave_period.py,Allocating leaves...,Urlaub zuordnen...
DocType: Program Enrollment,Vehicle/Bus Number,Fahrzeug / Bus Nummer
apps/erpnext/erpnext/public/js/call_popup/call_popup.js,Create New Contact,Neuen Kontakt erstellen
apps/erpnext/erpnext/education/doctype/course/course.js,Course Schedule,Kurstermine
@@ -1806,7 +1806,7 @@
DocType: Salary Component Account,Default Bank / Cash account will be automatically updated in Salary Journal Entry when this mode is selected.,"Standard Bank / Geldkonto wird automatisch in Gehalts Journal Entry aktualisiert werden, wenn dieser Modus ausgewählt ist."
DocType: Quiz,Latest Attempt,Letzter Versuch
DocType: Quiz Result,Quiz Result,Quiz-Ergebnis
-apps/erpnext/erpnext/hr/doctype/leave_allocation/leave_allocation.py,Total leaves allocated is mandatory for Leave Type {0},Die Gesamtzahl der zugewiesenen Blätter ist für Abwesenheitsart {0} erforderlich.
+apps/erpnext/erpnext/hr/doctype/leave_allocation/leave_allocation.py,Total leaves allocated is mandatory for Leave Type {0},Die Gesamtzahl der zugewiesenen Urlaube ist für Abwesenheitsart {0} erforderlich.
apps/erpnext/erpnext/controllers/sales_and_purchase_return.py,Row # {0}: Rate cannot be greater than the rate used in {1} {2},"Row # {0}: Die Rate kann nicht größer sein als die Rate, die in {1} {2}"
apps/erpnext/erpnext/utilities/user_progress.py,Meter,Meter
DocType: Workstation,Electricity Cost,Stromkosten
@@ -1916,7 +1916,7 @@
apps/erpnext/erpnext/templates/pages/order.js,Pay Remaining,Verbleibende Bezahlung
DocType: Purchase Invoice Item,Manufacturer,Hersteller
DocType: Landed Cost Item,Purchase Receipt Item,Kaufbeleg-Artikel
-DocType: Leave Allocation,Total Leaves Encashed,Insgesamt Blätter umkränzt
+DocType: Leave Allocation,Total Leaves Encashed,Summe ausbezahlter Urlaubstage
DocType: POS Profile,Sales Invoice Payment,Ausgangsrechnung-Zahlungen
DocType: Quality Inspection Template,Quality Inspection Template Name,Name der Qualitätsinspektionsvorlage
DocType: Project,First Email,Erste E-Mail
@@ -2540,7 +2540,7 @@
apps/erpnext/erpnext/hr/doctype/employee/employee.py,Employee cannot report to himself.,Mitarbeiter können nicht an sich selbst Bericht erstatten
apps/erpnext/erpnext/templates/pages/order.html,Rate:,Bewertung:
DocType: Bank Account,Change this date manually to setup the next synchronization start date,"Ändern Sie dieses Datum manuell, um das nächste Startdatum für die Synchronisierung festzulegen"
-DocType: Leave Type,Max Leaves Allowed,Max Blätter erlaubt
+DocType: Leave Type,Max Leaves Allowed,Höchstzahl erlaubter Urlaubstage
DocType: Account,"If the account is frozen, entries are allowed to restricted users.","Wenn das Konto gesperrt ist, sind einem eingeschränkten Benutzerkreis Buchungen erlaubt."
DocType: Email Digest,Bank Balance,Kontostand
apps/erpnext/erpnext/controllers/accounts_controller.py,Accounting Entry for {0}: {1} can only be made in currency: {2},Eine Buchung für {0}: {1} kann nur in der Währung: {2} vorgenommen werden
@@ -2990,7 +2990,7 @@
apps/erpnext/erpnext/config/support.py,Single unit of an Item.,Einzelnes Element eines Artikels
DocType: Fee Category,Fee Category,Gebührenkategorie
DocType: Agriculture Task,Next Business Day,Nächster Arbeitstag
-apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Allocated Leaves,Zugewiesene Blätter
+apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Allocated Leaves,Genehmigter Urlaub
DocType: Drug Prescription,Dosage by time interval,Dosierung nach Zeitintervall
apps/erpnext/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html,Total Taxable Value,Steuerpflichtiger Gesamtwert
DocType: Cash Flow Mapper,Section Header,Abschnitt Kopfzeile
@@ -3774,10 +3774,10 @@
DocType: Quality Inspection,Sample Size,Stichprobenumfang
apps/erpnext/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py,Please enter Receipt Document,Bitte geben Sie Eingangsbeleg
apps/erpnext/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py,All items have already been invoiced,Alle Artikel sind bereits abgerechnet
-apps/erpnext/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py,Leaves Taken,Blätter genommen
+apps/erpnext/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py,Leaves Taken,Urlaubstage genommen
apps/erpnext/erpnext/stock/doctype/packing_slip/packing_slip.py,Please specify a valid 'From Case No.',"Bitte eine eine gültige ""Von Fall Nr."" angeben"
apps/erpnext/erpnext/accounts/doctype/cost_center/cost_center_tree.js,Further cost centers can be made under Groups but entries can be made against non-Groups,"Weitere Kostenstellen können unter Gruppen angelegt werden, aber Buchungen können zu nicht-Gruppen erstellt werden"
-apps/erpnext/erpnext/hr/doctype/leave_allocation/leave_allocation.py,Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period,Die insgesamt zugewiesenen Blätter sind mehr Tage als die maximale Zuweisung von {0} Abwesenheitsart für den Mitarbeiter {1} in der Periode
+apps/erpnext/erpnext/hr/doctype/leave_allocation/leave_allocation.py,Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period,Die insgesamt zugewiesenen Urlaubstage sind mehr Tage als die maximale Zuweisung von {0} Abwesenheitsart für den Mitarbeiter {1} in der Periode
DocType: Branch,Branch,Betrieb
apps/erpnext/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html,"Other outward supplies(Nil rated,Exempted)","Sonstige Auslandslieferungen (ohne Rating, ausgenommen)"
DocType: Soil Analysis,Ca/(K+Ca+Mg),Ca / (K + Ca + Mg)
@@ -4097,7 +4097,7 @@
DocType: Cash Flow Mapping,Is Finance Cost Adjustment,Ist Finanzkostenanpassung
DocType: BOM,Operating Cost (Company Currency),Betriebskosten (Gesellschaft Währung)
DocType: Authorization Rule,Applicable To (Role),Anwenden auf (Rolle)
-apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Pending Leaves,Ausstehende Blätter
+apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Pending Leaves,Schwebende Urlaubstage
DocType: BOM Update Tool,Replace BOM,Erstelle Stückliste
apps/erpnext/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py,Code {0} already exist,Code {0} existiert bereits
DocType: Patient Encounter,Procedures,Verfahren
@@ -4342,7 +4342,7 @@
DocType: Sales Invoice,Is Return (Credit Note),ist Rücklieferung (Gutschrift)
apps/erpnext/erpnext/manufacturing/doctype/job_card/job_card.js,Start Job,Job starten
apps/erpnext/erpnext/assets/doctype/asset_movement/asset_movement.py,Serial no is required for the asset {0},Für Vermögenswert {0} ist eine Seriennr. Erforderlich.
-DocType: Leave Control Panel,Allocate Leaves,Blätter zuweisen
+DocType: Leave Control Panel,Allocate Leaves,Urlaubstage zuweisen
apps/erpnext/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py,Disabled template must not be default template,Deaktivierte Vorlage darf nicht Standardvorlage sein
DocType: Pricing Rule,Price or Product Discount,Preis- oder Produktrabatt
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.py,For row {0}: Enter planned qty,Für Zeile {0}: Geben Sie die geplante Menge ein
@@ -4440,7 +4440,7 @@
DocType: Crop,Scientific Name,Wissenschaftlicher Name
DocType: Healthcare Service Unit,Service Unit Type,Serviceeinheitstyp
DocType: Bank Account,Branch Code,Bankleitzahl / BIC
-apps/erpnext/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py,Total Leaves,insgesamt Blätter
+apps/erpnext/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py,Total Leaves,summe der Urlaubstage
DocType: Customer,"Reselect, if the chosen contact is edited after save","Wählen Sie erneut, wenn der ausgewählte Kontakt nach dem Speichern bearbeitet wird"
DocType: Quality Procedure,Parent Procedure,Übergeordnetes Verfahren
DocType: Patient Encounter,In print,in Druckbuchstaben
@@ -5658,7 +5658,7 @@
DocType: Depreciation Schedule,Finance Book Id,Finanzbuch-ID
DocType: Item,Safety Stock,Sicherheitsbestand
DocType: Healthcare Settings,Healthcare Settings,Gesundheitswesen
-apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Total Allocated Leaves,Insgesamt zugeteilte Blätter
+apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Total Allocated Leaves,Insgesamt zugeteilte Urlaubstage
apps/erpnext/erpnext/projects/doctype/task/task.py,Progress % for a task cannot be more than 100.,Fortschritt-% eines Vorgangs darf nicht größer 100 sein.
DocType: Stock Reconciliation Item,Before reconciliation,Vor Ausgleich
apps/erpnext/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py,To {0},An {0}
@@ -5769,7 +5769,7 @@
DocType: Normal Test Items,Normal Test Items,Normale Testartikel
DocType: QuickBooks Migrator,Company Settings,Unternehmenseinstellungen
DocType: Additional Salary,Overwrite Salary Structure Amount,Gehaltsstruktur überschreiben
-DocType: Leave Ledger Entry,Leaves,Blätter
+DocType: Leave Ledger Entry,Leaves,Urlaubstage
DocType: Student Language,Student Language,Student Sprache
DocType: Cash Flow Mapping,Is Working Capital,Ist Arbeitskapital
apps/erpnext/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js,Submit Proof,Nachweis einreichen
@@ -6362,7 +6362,7 @@
DocType: Sales Partner,Contact Desc,Kontakt-Beschr.
DocType: Email Digest,Send regular summary reports via Email.,Regelmäßig zusammenfassende Berichte per E-Mail senden.
apps/erpnext/erpnext/hr/doctype/expense_claim/expense_claim.py,Please set default account in Expense Claim Type {0},Bitte setzen Sie Standardkonto in Kostenabrechnung Typ {0}
-apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Available Leaves,Verfügbare Blätter
+apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Available Leaves,Verfügbare Urlaubstage
DocType: Assessment Result,Student Name,Name des Studenten
DocType: Hub Tracked Item,Item Manager,Artikel-Manager
apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py,Payroll Payable,Payroll Kreditoren
@@ -7508,7 +7508,7 @@
apps/erpnext/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py,Item {0} must be a stock Item,Artikel {0} muss ein Lagerartikel sein
DocType: Manufacturing Settings,Default Work In Progress Warehouse,Standard-Fertigungslager
apps/erpnext/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.js,"Schedules for {0} overlaps, do you want to proceed after skiping overlaped slots ?","Schedules für {0} Überlappungen, möchten Sie nach Überlappung überlappender Slots fortfahren?"
-apps/erpnext/erpnext/hr/doctype/leave_period/leave_period.js,Grant Leaves,Grant Blätter
+apps/erpnext/erpnext/hr/doctype/leave_period/leave_period.js,Grant Leaves,Urlaubstage gewähren
DocType: Restaurant,Default Tax Template,Standardsteuervorlage
apps/erpnext/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py,{0} Students have been enrolled,{0} Studenten wurden angemeldet
DocType: Fees,Student Details,Studenten Details
diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py
index f845cef..2099810 100644
--- a/erpnext/utilities/transaction_base.py
+++ b/erpnext/utilities/transaction_base.py
@@ -164,8 +164,8 @@
qty_fields = [qty_fields]
distinct_uoms = list(set([d.get(uom_field) for d in doc.get_all_children()]))
- integer_uoms = filter(lambda uom: frappe.db.get_value("UOM", uom,
- "must_be_whole_number", cache=True) or None, distinct_uoms)
+ integer_uoms = list(filter(lambda uom: frappe.db.get_value("UOM", uom,
+ "must_be_whole_number", cache=True) or None, distinct_uoms))
if not integer_uoms:
return