Merge branch 'develop' of https://github.com/frappe/erpnext into loan_top_up_fixes
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json
index 454c598..d4c1791 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.json
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json
@@ -302,10 +302,10 @@
"fieldname": "warehouse",
"fieldtype": "Link",
"label": "Warehouse",
+ "mandatory_depends_on": "update_stock",
"oldfieldname": "warehouse",
"oldfieldtype": "Link",
- "options": "Warehouse",
- "reqd": 1
+ "options": "Warehouse"
},
{
"default": "0",
@@ -350,4 +350,4 @@
],
"sort_field": "modified",
"sort_order": "DESC"
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 3dab054..71f2e12 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1619,22 +1619,23 @@
for pos_payment_method in pos_profile.get('payments'):
pos_payment_method = pos_payment_method.as_dict()
-
+
payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company)
- payment_mode[0].default = pos_payment_method.default
- append_payment(payment_mode[0])
+ if payment_mode:
+ payment_mode[0].default = pos_payment_method.default
+ append_payment(payment_mode[0])
def get_all_mode_of_payments(doc):
return frappe.db.sql("""
- select mpa.default_account, mpa.parent, mp.type as type
- from `tabMode of Payment Account` mpa,`tabMode of Payment` mp
+ select mpa.default_account, mpa.parent, mp.type as type
+ from `tabMode of Payment Account` mpa,`tabMode of Payment` mp
where mpa.parent = mp.name and mpa.company = %(company)s and mp.enabled = 1""",
{'company': doc.company}, as_dict=1)
def get_mode_of_payment_info(mode_of_payment, company):
return frappe.db.sql("""
- select mpa.default_account, mpa.parent, mp.type as type
- from `tabMode of Payment Account` mpa,`tabMode of Payment` mp
+ select mpa.default_account, mpa.parent, mp.type as type
+ from `tabMode of Payment Account` mpa,`tabMode of Payment` mp
where mpa.parent = mp.name and mpa.company = %s and mp.enabled = 1 and mp.name = %s""",
(company, mode_of_payment), as_dict=1)
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 964566a..9660c95 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -206,10 +206,19 @@
"rate": 14,
'included_in_print_rate': 1
})
+ si.append("taxes", {
+ "charge_type": "On Item Quantity",
+ "account_head": "_Test Account Education Cess - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "CESS",
+ "rate": 5,
+ 'included_in_print_rate': 1
+ })
si.insert()
# with inclusive tax
- self.assertEqual(si.net_total, 4385.96)
+ self.assertEqual(si.items[0].net_amount, 3947.368421052631)
+ self.assertEqual(si.net_total, 3947.37)
self.assertEqual(si.grand_total, 5000)
si.reload()
@@ -222,8 +231,8 @@
si.save()
# with inclusive tax and additional discount
- self.assertEqual(si.net_total, 4285.96)
- self.assertEqual(si.grand_total, 4885.99)
+ self.assertEqual(si.net_total, 3847.37)
+ self.assertEqual(si.grand_total, 4886)
si.reload()
@@ -235,7 +244,7 @@
si.save()
# with inclusive tax and additional discount
- self.assertEqual(si.net_total, 4298.25)
+ self.assertEqual(si.net_total, 3859.65)
self.assertEqual(si.grand_total, 4900.00)
def test_sales_invoice_discount_amount(self):
diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py
index 148357f..34facd8 100644
--- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py
+++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py
@@ -11,7 +11,7 @@
class AssetMaintenanceLog(Document):
def validate(self):
- if getdate(self.due_date) < getdate(nowdate()):
+ if getdate(self.due_date) < getdate(nowdate()) and self.maintenance_status not in ["Completed", "Cancelled"]:
self.maintenance_status = "Overdue"
if self.maintenance_status == "Completed" and not self.completion_date:
diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_list.js b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_list.js
index b854413..23000e6 100644
--- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_list.js
+++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_list.js
@@ -1,14 +1,15 @@
frappe.listview_settings['Asset Maintenance Log'] = {
add_fields: ["maintenance_status"],
+ has_indicator_for_draft: 1,
get_indicator: function(doc) {
- if(doc.maintenance_status=="Pending") {
- return [__("Pending"), "orange"];
- } else if(doc.maintenance_status=="Completed") {
- return [__("Completed"), "green"];
- } else if(doc.maintenance_status=="Cancelled") {
- return [__("Cancelled"), "red"];
- } else if(doc.maintenance_status=="Overdue") {
- return [__("Overdue"), "red"];
+ if (doc.maintenance_status=="Planned") {
+ return [__(doc.maintenance_status), "orange", "status,=," + doc.maintenance_status];
+ } else if (doc.maintenance_status=="Completed") {
+ return [__(doc.maintenance_status), "green", "status,=," + doc.maintenance_status];
+ } else if (doc.maintenance_status=="Cancelled") {
+ return [__(doc.maintenance_status), "red", "status,=," + doc.maintenance_status];
+ } else if (doc.maintenance_status=="Overdue") {
+ return [__(doc.maintenance_status), "red", "status,=," + doc.maintenance_status];
}
}
};
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 25065ab..9f2b971 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -94,7 +94,7 @@
if(this.frm.doc.status !== 'Closed' && flt(this.frm.doc.per_received) < 100 && flt(this.frm.doc.per_billed) < 100) {
this.frm.add_custom_button(__('Update Items'), () => {
erpnext.utils.update_child_items({
- frm: frm,
+ frm: this.frm,
child_docname: "items",
child_doctype: "Purchase Order Detail",
cannot_add_row: false,
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 66b5f30..3091193 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -985,7 +985,7 @@
# all rows about the reffered tax should be inclusive
_on_previous_row_error("1 - %d" % (tax.row_id,))
elif tax.get("category") == "Valuation":
- frappe.throw(_("Valuation type charges can not marked as Inclusive"))
+ frappe.throw(_("Valuation type charges can not be marked as Inclusive"))
def set_balance_in_account_currency(gl_dict, account_currency=None, conversion_rate=None, company_currency=None):
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 572e1ca..2a14be8 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -161,8 +161,9 @@
for item in self.doc.get("items"):
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
cumulated_tax_fraction = 0
+ total_inclusive_tax_amount_per_qty = 0
for i, tax in enumerate(self.doc.get("taxes")):
- tax.tax_fraction_for_current_item = self.get_current_tax_fraction(tax, item_tax_map)
+ tax.tax_fraction_for_current_item, inclusive_tax_amount_per_qty = self.get_current_tax_fraction(tax, item_tax_map)
if i==0:
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
@@ -172,9 +173,12 @@
+ tax.tax_fraction_for_current_item
cumulated_tax_fraction += tax.tax_fraction_for_current_item
+ total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
- if cumulated_tax_fraction and not self.discount_amount_applied and item.qty:
- item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction))
+ if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
+ amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
+
+ item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
item.discount_percentage = flt(item.discount_percentage,
item.precision("discount_percentage"))
@@ -190,6 +194,7 @@
from tax inclusive amount
"""
current_tax_fraction = 0
+ inclusive_tax_amount_per_qty = 0
if cint(tax.included_in_print_rate):
tax_rate = self._get_tax_rate(tax, item_tax_map)
@@ -204,10 +209,15 @@
elif tax.charge_type == "On Previous Row Total":
current_tax_fraction = (tax_rate / 100.0) * \
self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
+
+ elif tax.charge_type == "On Item Quantity":
+ inclusive_tax_amount_per_qty = flt(tax_rate)
- if getattr(tax, "add_deduct_tax", None):
- current_tax_fraction *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
- return current_tax_fraction
+ if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
+ current_tax_fraction *= -1.0
+ inclusive_tax_amount_per_qty *= -1.0
+
+ return current_tax_fraction, inclusive_tax_amount_per_qty
def _get_tax_rate(self, tax, item_tax_map):
if tax.account_head in item_tax_map:
@@ -321,7 +331,7 @@
current_tax_amount = (tax_rate / 100.0) * \
self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
elif tax.charge_type == "On Item Quantity":
- current_tax_amount = tax_rate * item.stock_qty
+ current_tax_amount = tax_rate * item.qty
self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
@@ -472,7 +482,7 @@
actual_taxes_dict = {}
for tax in self.doc.get("taxes"):
- if tax.charge_type == "Actual":
+ if tax.charge_type in ["Actual", "On Item Quantity"]:
tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
actual_taxes_dict.setdefault(tax.idx, tax_amount)
elif tax.row_id in actual_taxes_dict:
diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json
index 219ed89..b61cad3 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.json
+++ b/erpnext/crm/doctype/opportunity/opportunity.json
@@ -50,9 +50,9 @@
"contact_email",
"contact_mobile",
"more_info",
+ "company",
"campaign",
"column_break1",
- "company",
"transaction_date",
"amended_from",
"lost_reasons"
@@ -344,7 +344,7 @@
"collapsible": 1,
"fieldname": "more_info",
"fieldtype": "Section Break",
- "label": "Source",
+ "label": "More Information",
"oldfieldtype": "Section Break",
"options": "fa fa-file-text"
},
@@ -411,7 +411,7 @@
"fieldname": "lost_reasons",
"fieldtype": "Table MultiSelect",
"label": "Lost Reasons",
- "options": "Lost Reason Detail",
+ "options": "Opportunity Lost Reason Detail",
"read_only": 1
},
{
@@ -424,7 +424,7 @@
"icon": "fa fa-info-sign",
"idx": 195,
"links": [],
- "modified": "2020-08-11 14:49:13.496297",
+ "modified": "2020-08-11 17:34:35.066961",
"modified_by": "Administrator",
"module": "CRM",
"name": "Opportunity",
diff --git a/erpnext/crm/doctype/opportunity_lost_reason_detail/__init__.py b/erpnext/crm/doctype/opportunity_lost_reason_detail/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/crm/doctype/opportunity_lost_reason_detail/__init__.py
diff --git a/erpnext/crm/doctype/opportunity_lost_reason_detail/opportunity_lost_reason_detail.json b/erpnext/crm/doctype/opportunity_lost_reason_detail/opportunity_lost_reason_detail.json
new file mode 100644
index 0000000..50620e2
--- /dev/null
+++ b/erpnext/crm/doctype/opportunity_lost_reason_detail/opportunity_lost_reason_detail.json
@@ -0,0 +1,31 @@
+{
+ "actions": [],
+ "creation": "2020-07-16 16:11:39.830389",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "lost_reason"
+ ],
+ "fields": [
+ {
+ "fieldname": "lost_reason",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Opportunity Lost Reason",
+ "options": "Opportunity Lost Reason"
+ }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-07-26 17:58:26.313242",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Opportunity Lost Reason Detail",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/opportunity_lost_reason_detail/opportunity_lost_reason_detail.py b/erpnext/crm/doctype/opportunity_lost_reason_detail/opportunity_lost_reason_detail.py
new file mode 100644
index 0000000..8723f1d
--- /dev/null
+++ b/erpnext/crm/doctype/opportunity_lost_reason_detail/opportunity_lost_reason_detail.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class OpportunityLostReasonDetail(Document):
+ pass
diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json
index 0fed8d3..895cf72 100644
--- a/erpnext/hr/desk_page/hr/hr.json
+++ b/erpnext/hr/desk_page/hr/hr.json
@@ -78,7 +78,7 @@
"idx": 0,
"is_standard": 1,
"label": "HR",
- "modified": "2020-06-16 19:20:50.976045",
+ "modified": "2020-08-11 17:04:38.655417",
"modified_by": "Administrator",
"module": "HR",
"name": "HR",
@@ -88,7 +88,7 @@
"pin_to_top": 0,
"shortcuts": [
{
- "color": "#9deca2",
+ "color": "#cef6d1",
"format": "{} Active",
"label": "Employee",
"link_to": "Employee",
@@ -96,12 +96,7 @@
"type": "DocType"
},
{
- "label": "Attendance",
- "link_to": "Attendance",
- "stats_filter": "",
- "type": "DocType"
- },
- {
+ "color": "#ffe8cd",
"format": "{} Open",
"label": "Leave Application",
"link_to": "Leave Application",
@@ -109,6 +104,12 @@
"type": "DocType"
},
{
+ "label": "Attendance",
+ "link_to": "Attendance",
+ "stats_filter": "",
+ "type": "DocType"
+ },
+ {
"label": "Job Applicant",
"link_to": "Job Applicant",
"type": "DocType"
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js
index 210a73c..e9e129c 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js
@@ -5,20 +5,23 @@
frappe.ui.form.on("Leave Allocation", {
onload: function(frm) {
+ // Ignore cancellation of doctype on cancel all.
+ frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"];
+
if(!frm.doc.from_date) frm.set_value("from_date", frappe.datetime.get_today());
frm.set_query("employee", function() {
return {
query: "erpnext.controllers.queries.employee_query"
- }
+ };
});
frm.set_query("leave_type", function() {
return {
filters: {
is_lwp: 0
}
- }
- })
+ };
+ });
},
refresh: function(frm) {
diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js
index 4001a45..d62e418 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.js
+++ b/erpnext/hr/doctype/leave_application/leave_application.js
@@ -19,6 +19,10 @@
frm.set_query("employee", erpnext.queries.employee);
},
onload: function(frm) {
+
+ // Ignore cancellation of doctype on cancel all.
+ frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"];
+
if (!frm.doc.posting_date) {
frm.set_value("posting_date", frappe.datetime.get_today());
}
diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.js b/erpnext/hr/doctype/leave_encashment/leave_encashment.js
index 701c2f0..71a3422 100644
--- a/erpnext/hr/doctype/leave_encashment/leave_encashment.js
+++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.js
@@ -2,6 +2,10 @@
// For license information, please see license.txt
frappe.ui.form.on('Leave Encashment', {
+ onload: function(frm) {
+ // Ignore cancellation of doctype on cancel all.
+ frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"];
+ },
setup: function(frm) {
frm.set_query("leave_type", function() {
return {
@@ -33,7 +37,7 @@
doc: frm.doc,
callback: function(r) {
frm.refresh_fields();
- }
+ }
});
}
}
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py
index 832557e..bac6e63 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.py
@@ -16,14 +16,16 @@
class LoanApplication(Document):
def validate(self):
-
- validate_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount,
- self.repayment_periods, self.is_term_loan)
-
- self.validate_loan_type()
self.set_pledge_amount()
self.set_loan_amount()
self.validate_loan_amount()
+
+ if self.is_term_loan:
+ validate_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount,
+ self.repayment_periods, self.is_term_loan)
+
+ self.validate_loan_type()
+
self.get_repayment_details()
self.check_sanctioned_amount_limit()
@@ -106,7 +108,7 @@
if self.is_secured_loan and self.proposed_pledges:
self.maximum_loan_amount = 0
for security in self.proposed_pledges:
- self.maximum_loan_amount += security.post_haircut_amount
+ self.maximum_loan_amount += flt(security.post_haircut_amount)
if not self.loan_amount and self.is_secured_loan and self.proposed_pledges:
self.loan_amount = self.maximum_loan_amount
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 49af0ba..361fe83 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -718,3 +718,6 @@
erpnext.patches.v12_0.update_item_tax_template_company
erpnext.patches.v13_0.move_branch_code_to_bank_account
erpnext.patches.v13_0.healthcare_lab_module_rename_doctypes
+erpnext.patches.v13_0.stock_entry_enhancements
+erpnext.patches.v12_0.update_state_code_for_daman_and_diu
+erpnext.patches.v12_0.rename_lost_reason_detail
diff --git a/erpnext/patches/v12_0/rename_lost_reason_detail.py b/erpnext/patches/v12_0/rename_lost_reason_detail.py
new file mode 100644
index 0000000..044d023
--- /dev/null
+++ b/erpnext/patches/v12_0/rename_lost_reason_detail.py
@@ -0,0 +1,17 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ if frappe.db.exists("DocType", "Lost Reason Detail"):
+ frappe.reload_doc("crm", "doctype", "opportunity_lost_reason_detail")
+ frappe.reload_doc("setup", "doctype", "quotation_lost_reason_detail")
+
+ frappe.db.sql("""INSERT INTO `tabOpportunity Lost Reason Detail` SELECT * FROM `tabLost Reason Detail` WHERE `parenttype` = 'Opportunity'""")
+
+ frappe.db.sql("""INSERT INTO `tabQuotation Lost Reason Detail` SELECT * FROM `tabLost Reason Detail` WHERE `parenttype` = 'Quotation'""")
+
+ frappe.db.sql("""INSERT INTO `tabQuotation Lost Reason` (`name`, `creation`, `modified`, `modified_by`, `owner`, `docstatus`, `parent`, `parentfield`, `parenttype`, `idx`, `_comments`, `_assign`, `_user_tags`, `_liked_by`, `order_lost_reason`)
+ SELECT o.`name`, o.`creation`, o.`modified`, o.`modified_by`, o.`owner`, o.`docstatus`, o.`parent`, o.`parentfield`, o.`parenttype`, o.`idx`, o.`_comments`, o.`_assign`, o.`_user_tags`, o.`_liked_by`, o.`lost_reason`
+ FROM `tabOpportunity Lost Reason` o LEFT JOIN `tabQuotation Lost Reason` q ON q.name = o.name WHERE q.name IS NULL""")
+
+ frappe.delete_doc("DocType", "Lost Reason Detail")
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/stock_entry_enhancements.py b/erpnext/patches/v12_0/stock_entry_enhancements.py
index d04b3d3..847d928 100644
--- a/erpnext/patches/v12_0/stock_entry_enhancements.py
+++ b/erpnext/patches/v12_0/stock_entry_enhancements.py
@@ -19,7 +19,7 @@
for purpose in ["Material Issue", "Material Receipt", "Material Transfer",
"Material Transfer for Manufacture", "Material Consumption for Manufacture", "Manufacture",
- "Repack", "Send to Subcontractor", "Send to Warehouse", "Receive at Warehouse"]:
+ "Repack", "Send to Subcontractor"]:
ste_type = frappe.get_doc({
'doctype': 'Stock Entry Type',
diff --git a/erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py b/erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py
new file mode 100644
index 0000000..7450e9c
--- /dev/null
+++ b/erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py
@@ -0,0 +1,22 @@
+import frappe
+from erpnext.regional.india import states
+
+def execute():
+
+ company = frappe.get_all('Company', filters = {'country': 'India'})
+ if not company:
+ return
+
+ # Update options in gst_state custom field
+ gst_state = frappe.get_doc('Custom Field', 'Address-gst_state')
+ gst_state.options = '\n'.join(states)
+ gst_state.save()
+
+ # Update gst_state and state code in existing address
+ frappe.db.sql("""
+ UPDATE `tabAddress`
+ SET
+ gst_state = 'Dadra and Nagar Haveli and Daman and Diu',
+ gst_state_number = 26
+ WHERE gst_state = 'Daman and Diu'
+ """)
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/stock_entry_enhancements.py b/erpnext/patches/v13_0/stock_entry_enhancements.py
new file mode 100644
index 0000000..dcc4f95
--- /dev/null
+++ b/erpnext/patches/v13_0/stock_entry_enhancements.py
@@ -0,0 +1,27 @@
+# Copyright(c) 2020, Frappe Technologies Pvt.Ltd.and Contributors
+# License: GNU General Public License v3.See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc("stock", "doctype", "stock_entry")
+ if frappe.db.has_column("Stock Entry", "add_to_transit"):
+ frappe.db.sql("""
+ UPDATE `tabStock Entry` SET
+ stock_entry_type = 'Material Transfer',
+ purpose = 'Material Transfer',
+ add_to_transit = 1 WHERE stock_entry_type = 'Send to Warehouse'
+ """)
+
+ frappe.db.sql("""UPDATE `tabStock Entry` SET
+ stock_entry_type = 'Material Transfer',
+ purpose = 'Material Transfer'
+ WHERE stock_entry_type = 'Receive at Warehouse'
+ """)
+
+ frappe.reload_doc("stock", "doctype", "warehouse_type")
+ if not frappe.db.exists('Warehouse Type', 'Transit'):
+ doc = frappe.new_doc('Warehouse Type')
+ doc.name = 'Transit'
+ doc.insert()
\ No newline at end of file
diff --git a/erpnext/payroll/desk_page/payroll/payroll.json b/erpnext/payroll/desk_page/payroll/payroll.json
index b5eac46..285e3b3 100644
--- a/erpnext/payroll/desk_page/payroll/payroll.json
+++ b/erpnext/payroll/desk_page/payroll/payroll.json
@@ -8,7 +8,7 @@
{
"hidden": 0,
"label": "Taxation",
- "links": "[\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n \n }\n]"
+ "links": "[\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Other Income\",\n \"name\": \"Employee Other Income\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n \n }\n]"
},
{
"hidden": 0,
@@ -38,7 +38,7 @@
"idx": 0,
"is_standard": 1,
"label": "Payroll",
- "modified": "2020-06-19 12:23:06.034046",
+ "modified": "2020-08-10 19:38:45.976209",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Payroll",
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 5de2930..607c3fd 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -94,13 +94,6 @@
}
},
- company: function(frm) {
- frappe.db.get_value('Company', { 'company_name' : frm.doc.company }, 'standard_working_hours')
- .then(({ message }) => {
- (frappe.working_hours = message.standard_working_hours || 0);
- });
- },
-
make_invoice: function(frm) {
let dialog = new frappe.ui.Dialog({
title: __("Select Item (optional)"),
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 405a33c..6951539 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -163,9 +163,11 @@
$.each(me.frm.doc["items"] || [], function(n, item) {
var item_tax_map = me._load_item_tax_rate(item.item_tax_rate);
var cumulated_tax_fraction = 0.0;
-
+ var total_inclusive_tax_amount_per_qty = 0;
$.each(me.frm.doc["taxes"] || [], function(i, tax) {
- tax.tax_fraction_for_current_item = me.get_current_tax_fraction(tax, item_tax_map);
+ var current_tax_fraction = me.get_current_tax_fraction(tax, item_tax_map);
+ tax.tax_fraction_for_current_item = current_tax_fraction[0];
+ var inclusive_tax_amount_per_qty = current_tax_fraction[1];
if(i==0) {
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item;
@@ -176,10 +178,12 @@
}
cumulated_tax_fraction += tax.tax_fraction_for_current_item;
+ total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty);
});
- if(cumulated_tax_fraction && !me.discount_amount_applied) {
- item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction));
+ if(!me.discount_amount_applied && item.qty && (total_inclusive_tax_amount_per_qty || cumulated_tax_fraction)) {
+ var amount = flt(item.amount) - total_inclusive_tax_amount_per_qty;
+ item.net_amount = flt(amount / (1 + cumulated_tax_fraction));
item.net_rate = item.qty ? flt(item.net_amount / item.qty, precision("net_rate", item)) : 0;
me.set_in_company_currency(item, ["net_rate", "net_amount"]);
@@ -191,6 +195,7 @@
// Get tax fraction for calculating tax exclusive amount
// from tax inclusive amount
var current_tax_fraction = 0.0;
+ var inclusive_tax_amount_per_qty = 0;
if(cint(tax.included_in_print_rate)) {
var tax_rate = this._get_tax_rate(tax, item_tax_map);
@@ -205,13 +210,16 @@
} else if(tax.charge_type == "On Previous Row Total") {
current_tax_fraction = (tax_rate / 100.0) *
this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_fraction_for_current_item;
+ } else if (tax.charge_type == "On Item Quantity") {
+ inclusive_tax_amount_per_qty = flt(tax_rate);
}
}
- if(tax.add_deduct_tax) {
- current_tax_fraction *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
+ if(tax.add_deduct_tax && tax.add_deduct_tax == "Deduct") {
+ current_tax_fraction *= -1;
+ inclusive_tax_amount_per_qty *= -1;
}
- return current_tax_fraction;
+ return [current_tax_fraction, inclusive_tax_amount_per_qty];
},
_get_tax_rate: function(tax, item_tax_map) {
@@ -360,8 +368,9 @@
} else if(tax.charge_type == "On Previous Row Total") {
current_tax_amount = (tax_rate / 100.0) *
this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_for_current_item;
+ } else if (tax.charge_type == "On Item Quantity") {
+ current_tax_amount = tax_rate * item.qty;
}
-
this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
return current_tax_amount;
@@ -573,7 +582,7 @@
var actual_taxes_dict = {};
$.each(this.frm.doc["taxes"] || [], function(i, tax) {
- if (tax.charge_type == "Actual") {
+ if (in_list(["Actual", "On Item Quantity"], tax.charge_type)) {
var tax_amount = (tax.category == "Valuation") ? 0.0 : tax.tax_amount;
tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
actual_taxes_dict[tax.idx] = tax_amount;
@@ -586,7 +595,7 @@
$.each(actual_taxes_dict, function(key, value) {
if (value) total_actual_tax += value;
});
-
+
return flt(this.frm.doc.grand_total - total_actual_tax, precision("grand_total"));
}
},
diff --git a/erpnext/regional/india/__init__.py b/erpnext/regional/india/__init__.py
index 0ed98b7..d6221a8 100644
--- a/erpnext/regional/india/__init__.py
+++ b/erpnext/regional/india/__init__.py
@@ -10,8 +10,7 @@
'Bihar',
'Chandigarh',
'Chhattisgarh',
- 'Dadra and Nagar Haveli',
- 'Daman and Diu',
+ 'Dadra and Nagar Haveli and Daman and Diu',
'Delhi',
'Goa',
'Gujarat',
@@ -50,8 +49,7 @@
"Bihar": "10",
"Chandigarh": "04",
"Chhattisgarh": "22",
- "Dadra and Nagar Haveli": "26",
- "Daman and Diu": "25",
+ "Dadra and Nagar Haveli and Daman and Diu": "26",
"Delhi": "07",
"Goa": "30",
"Gujarat": "24",
diff --git a/erpnext/regional/india/gst_state_code_data.json b/erpnext/regional/india/gst_state_code_data.json
index 6dab81d..ff88e0f 100644
--- a/erpnext/regional/india/gst_state_code_data.json
+++ b/erpnext/regional/india/gst_state_code_data.json
@@ -135,14 +135,9 @@
"state_name": "Delhi"
},
{
- "state_number": "25",
- "state_code": "DD",
- "state_name": "Daman and Diu"
- },
- {
"state_number": "26",
"state_code": "DN",
- "state_name": "Dadra and Nagar Haveli"
+ "state_name": "Dadra and Nagar Haveli and Daman and Diu"
},
{
"state_number": "22",
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index ca62488..93d4832 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -184,6 +184,14 @@
def validate_credit_limit_on_change(self):
if self.get("__islocal") or not self.credit_limits:
return
+
+ past_credit_limits = [d.credit_limit
+ for d in frappe.db.get_all("Customer Credit Limit", filters={'parent': self.name}, fields=["credit_limit"], order_by="company")]
+
+ current_credit_limits = [d.credit_limit for d in sorted(self.credit_limits, key=lambda k: k.company)]
+
+ if past_credit_limits == current_credit_limits:
+ return
company_record = []
for limit in self.credit_limits:
diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json
index 6d34c2a..5b85187 100644
--- a/erpnext/selling/doctype/quotation/quotation.json
+++ b/erpnext/selling/doctype/quotation/quotation.json
@@ -923,7 +923,7 @@
"fieldname": "lost_reasons",
"fieldtype": "Table MultiSelect",
"label": "Lost Reasons",
- "options": "Lost Reason Detail",
+ "options": "Quotation Lost Reason Detail",
"read_only": 1
}
],
@@ -932,7 +932,7 @@
"is_submittable": 1,
"links": [],
"max_attachments": 1,
- "modified": "2020-07-18 04:59:09.960118",
+ "modified": "2020-07-26 17:46:19.951223",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 449a968..ab095eb 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -68,7 +68,7 @@
def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None):
if not self.has_sales_order():
- get_lost_reasons = frappe.get_list('Opportunity Lost Reason',
+ get_lost_reasons = frappe.get_list('Quotation Lost Reason',
fields = ["name"])
lost_reasons_lst = [reason.get('name') for reason in get_lost_reasons]
frappe.db.set(self, 'status', 'Lost')
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index 333a563..002cfe4 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -494,13 +494,18 @@
var dialog = new frappe.ui.Dialog({
title: __("Set as Lost"),
fields: [
- {"fieldtype": "Table MultiSelect",
- "label": __("Lost Reasons"),
- "fieldname": "lost_reason",
- "options": "Lost Reason Detail",
- "reqd": 1},
-
- {"fieldtype": "Text", "label": __("Detailed Reason"), "fieldname": "detailed_reason"},
+ {
+ "fieldtype": "Table MultiSelect",
+ "label": __("Lost Reasons"),
+ "fieldname": "lost_reason",
+ "options": frm.doctype === 'Opportunity' ? 'Opportunity Lost Reason Detail': 'Quotation Lost Reason Detail',
+ "reqd": 1
+ },
+ {
+ "fieldtype": "Text",
+ "label": __("Detailed Reason"),
+ "fieldname": "detailed_reason"
+ },
],
primary_action: function() {
var values = dialog.get_values();
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 7ae5385..f882db6 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -34,6 +34,16 @@
frm.set_query("default_buying_terms", function() {
return { filters: { buying: 1 } };
});
+
+ frm.set_query("default_in_transit_warehouse", function() {
+ return {
+ filters:{
+ 'warehouse_type' : 'Transit',
+ 'is_group': 0,
+ 'company': frm.doc.company
+ }
+ };
+ });
},
company_name: function(frm) {
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 03703fd..4a26a71 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -25,6 +25,7 @@
"default_selling_terms",
"default_buying_terms",
"default_warehouse_for_sales_return",
+ "default_in_transit_warehouse",
"column_break_10",
"country",
"create_chart_of_accounts_based_on",
@@ -733,6 +734,12 @@
"fieldname": "enable_perpetual_inventory_for_non_stock_items",
"fieldtype": "Check",
"label": "Enable Perpetual Inventory For Non Stock Items"
+ },
+ {
+ "fieldname": "default_in_transit_warehouse",
+ "fieldtype": "Link",
+ "label": "Default In Transit Warehouse",
+ "options": "Warehouse"
}
],
"icon": "fa fa-building",
@@ -740,7 +747,7 @@
"image_field": "company_logo",
"is_tree": 1,
"links": [],
- "modified": "2020-06-24 12:45:31.462195",
+ "modified": "2020-08-06 00:38:08.311216",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 47b41a9..8e707fe 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -140,7 +140,8 @@
{"warehouse_name": _("All Warehouses"), "is_group": 1},
{"warehouse_name": _("Stores"), "is_group": 0},
{"warehouse_name": _("Work In Progress"), "is_group": 0},
- {"warehouse_name": _("Finished Goods"), "is_group": 0}]:
+ {"warehouse_name": _("Finished Goods"), "is_group": 0},
+ {"warehouse_name": _("Goods In Transit"), "is_group": 0, "warehouse_type": "Transit"}]:
if not frappe.db.exists("Warehouse", "{0} - {1}".format(wh_detail["warehouse_name"], self.abbr)):
warehouse = frappe.get_doc({
@@ -149,7 +150,8 @@
"is_group": wh_detail["is_group"],
"company": self.name,
"parent_warehouse": "{0} - {1}".format(_("All Warehouses"), self.abbr) \
- if not wh_detail["is_group"] else ""
+ if not wh_detail["is_group"] else "",
+ "warehouse_type" : wh_detail["warehouse_type"] if "warehouse_type" in wh_detail else None
})
warehouse.flags.ignore_permissions = True
warehouse.flags.ignore_mandatory = True
diff --git a/erpnext/setup/doctype/quotation_lost_reason_detail/__init__.py b/erpnext/setup/doctype/quotation_lost_reason_detail/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/setup/doctype/quotation_lost_reason_detail/__init__.py
diff --git a/erpnext/setup/doctype/quotation_lost_reason_detail/quotation_lost_reason_detail.json b/erpnext/setup/doctype/quotation_lost_reason_detail/quotation_lost_reason_detail.json
new file mode 100644
index 0000000..5432141
--- /dev/null
+++ b/erpnext/setup/doctype/quotation_lost_reason_detail/quotation_lost_reason_detail.json
@@ -0,0 +1,31 @@
+{
+ "actions": [],
+ "creation": "2020-07-14 09:21:44.057724",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "lost_reason"
+ ],
+ "fields": [
+ {
+ "fieldname": "lost_reason",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Quotation Lost Reason",
+ "options": "Quotation Lost Reason"
+ }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-07-26 17:58:56.373775",
+ "modified_by": "Administrator",
+ "module": "Setup",
+ "name": "Quotation Lost Reason Detail",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/quotation_lost_reason_detail/quotation_lost_reason_detail.py b/erpnext/setup/doctype/quotation_lost_reason_detail/quotation_lost_reason_detail.py
new file mode 100644
index 0000000..7bb8d02
--- /dev/null
+++ b/erpnext/setup/doctype/quotation_lost_reason_detail/quotation_lost_reason_detail.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class QuotationLostReasonDetail(Document):
+ pass
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index ad063cf..72ed002 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -95,8 +95,6 @@
{'doctype': 'Stock Entry Type', 'name': 'Send to Subcontractor', 'purpose': 'Send to Subcontractor'},
{'doctype': 'Stock Entry Type', 'name': 'Material Transfer for Manufacture', 'purpose': 'Material Transfer for Manufacture'},
{'doctype': 'Stock Entry Type', 'name': 'Material Consumption for Manufacture', 'purpose': 'Material Consumption for Manufacture'},
- {'doctype': 'Stock Entry Type', 'name': 'Send to Warehouse', 'purpose': 'Send to Warehouse'},
- {'doctype': 'Stock Entry Type', 'name': 'Receive at Warehouse', 'purpose': 'Receive at Warehouse'},
# Designation
{'doctype': 'Designation', 'designation_name': _('CEO')},
@@ -244,7 +242,10 @@
{"doctype": "Sales Stage", "stage_name": _("Identifying Decision Makers")},
{"doctype": "Sales Stage", "stage_name": _("Perception Analysis")},
{"doctype": "Sales Stage", "stage_name": _("Proposal/Price Quote")},
- {"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")}
+ {"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")},
+
+ # Warehouse Type
+ {'doctype': 'Warehouse Type', 'name': 'Transit'},
]
from erpnext.setup.setup_wizard.data.industry_type import get_industry_types
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 45526a3..d07b3dc 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -123,6 +123,7 @@
"weightage",
"slideshow",
"website_image",
+ "website_image_alt",
"thumbnail",
"cb72",
"website_warehouse",
@@ -1054,15 +1055,21 @@
"fieldtype": "Data",
"label": "Default Manufacturer Part No",
"read_only": 1
+ },
+ {
+ "fieldname": "website_image_alt",
+ "fieldtype": "Data",
+ "label": "Image Description"
}
],
"has_web_view": 1,
"icon": "fa fa-tag",
"idx": 2,
"image_field": "image",
+ "index_web_pages_for_search": 1,
"links": [],
"max_attachments": 1,
- "modified": "2020-07-31 21:21:10.956453",
+ "modified": "2020-08-07 14:24:58.384992",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
@@ -1124,4 +1131,4 @@
"sort_order": "DESC",
"title_field": "item_name",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index d7b43bf..d209f48 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -343,7 +343,7 @@
if variant:
context.variant = frappe.get_doc("Item", variant)
- for fieldname in ("website_image", "web_long_description", "description",
+ for fieldname in ("website_image", "website_image_alt", "web_long_description", "description",
"website_specifications"):
if context.variant.get(fieldname):
value = context.variant.get(fieldname)
diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json
index d1f29e3..44503d2 100644
--- a/erpnext/stock/doctype/material_request/material_request.json
+++ b/erpnext/stock/doctype/material_request/material_request.json
@@ -11,6 +11,7 @@
"naming_series",
"title",
"material_request_type",
+ "transfer_status",
"customer",
"column_break_2",
"schedule_date",
@@ -303,13 +304,22 @@
"fieldtype": "Link",
"label": "Set From Warehouse",
"options": "Warehouse"
+ },
+ {
+ "allow_on_submit": 1,
+ "depends_on": "eval:doc.add_to_transit == 1",
+ "fieldname": "transfer_status",
+ "fieldtype": "Select",
+ "label": "Transfer Status",
+ "options": "\nNot Started\nIn Transit\nCompleted",
+ "read_only": 1
}
],
"icon": "fa fa-ticket",
"idx": 70,
"is_submittable": 1,
"links": [],
- "modified": "2020-05-01 20:21:09.990867",
+ "modified": "2020-08-10 13:27:54.891058",
"modified_by": "Administrator",
"module": "Stock",
"name": "Material Request",
diff --git a/erpnext/stock/doctype/material_request/material_request_list.js b/erpnext/stock/doctype/material_request/material_request_list.js
index 614ecb8..0d70958 100644
--- a/erpnext/stock/doctype/material_request/material_request_list.js
+++ b/erpnext/stock/doctype/material_request/material_request_list.js
@@ -1,8 +1,16 @@
frappe.listview_settings['Material Request'] = {
- add_fields: ["material_request_type", "status", "per_ordered", "per_received"],
+ add_fields: ["material_request_type", "status", "per_ordered", "per_received", "transfer_status"],
get_indicator: function(doc) {
if(doc.status=="Stopped") {
return [__("Stopped"), "red", "status,=,Stopped"];
+ } else if(doc.transfer_status && doc.docstatus != 2) {
+ if (doc.transfer_status == "Not Started") {
+ return [__("Not Started"), "orange"];
+ } else if (doc.transfer_status == "In Transit") {
+ return [__("In Transit"), "yellow"];
+ } else if (doc.transfer_status == "Completed") {
+ return [__("Completed"), "green"];
+ }
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 0) {
return [__("Pending"), "orange", "per_ordered,=,0"];
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) < 100) {
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 53b986c..9845bc2 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -19,7 +19,6 @@
filters: [
['Stock Entry', 'docstatus', '=', 1],
['Stock Entry', 'per_transferred', '<','100'],
- ['Stock Entry', 'purpose', '=', 'Send to Warehouse']
]
}
});
@@ -171,9 +170,9 @@
}
}
- if (frm.doc.docstatus === 1 && frm.doc.purpose == 'Send to Warehouse') {
- if (frm.doc.per_transferred < 100) {
- frm.add_custom_button(__('Receive at Warehouse Entry'), function() {
+ if (frm.doc.docstatus === 1) {
+ if (frm.doc.add_to_transit && frm.doc.purpose=='Material Transfer' && frm.doc.per_transferred < 100) {
+ frm.add_custom_button('End Transit', function() {
frappe.model.open_mapped_doc({
method: "erpnext.stock.doctype.stock_entry.stock_entry.make_stock_in_entry",
frm: frm
@@ -266,6 +265,7 @@
stock_entry_type: function(frm){
frm.remove_custom_button('Bill of Materials', "Get items from");
frm.events.show_bom_custom_button(frm);
+ frm.trigger('add_to_transit');
},
purpose: function(frm) {
@@ -532,6 +532,26 @@
target_warehouse_address: function(frm) {
erpnext.utils.get_address_display(frm, 'target_warehouse_address', 'target_address_display', false);
+ },
+
+ add_to_transit: function(frm) {
+ if(frm.doc.add_to_transit && frm.doc.purpose=='Material Transfer') {
+ frm.set_value('stock_entry_type', 'Material Transfer');
+ frm.fields_dict.to_warehouse.get_query = function() {
+ return {
+ filters:{
+ 'warehouse_type' : 'Transit',
+ 'is_group': 0,
+ 'company': frm.doc.company
+ }
+ };
+ };
+ frappe.db.get_value('Company', frm.doc.company, 'default_in_transit_warehouse', (r) => {
+ if (r.default_in_transit_warehouse) {
+ frm.set_value('to_warehouse', r.default_in_transit_warehouse);
+ }
+ });
+ }
}
})
@@ -754,6 +774,7 @@
}
erpnext.hide_company();
erpnext.utils.add_item(this.frm);
+ this.frm.trigger('add_to_transit');
},
scan_barcode: function() {
@@ -919,8 +940,6 @@
doc.purpose!='Material Issue');
this.frm.fields_dict["items"].grid.set_column_disp("additional_cost", doc.purpose!='Material Issue');
- this.frm.toggle_reqd("outgoing_stock_entry",
- doc.purpose == 'Receive at Warehouse' ? 1: 0);
},
supplier: function(doc) {
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index 704ae41..61e0df6 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -13,6 +13,7 @@
"stock_entry_type",
"outgoing_stock_entry",
"purpose",
+ "add_to_transit",
"work_order",
"purchase_order",
"delivery_note_no",
@@ -116,11 +117,12 @@
"reqd": 1
},
{
- "depends_on": "eval:doc.purpose == 'Receive at Warehouse'",
+ "depends_on": "eval:doc.purpose == 'Material Transfer'",
"fieldname": "outgoing_stock_entry",
"fieldtype": "Link",
"label": "Stock Entry (Outward GIT)",
- "options": "Stock Entry"
+ "options": "Stock Entry",
+ "read_only": 1
},
{
"bold": 1,
@@ -132,7 +134,7 @@
"label": "Purpose",
"oldfieldname": "purpose",
"oldfieldtype": "Select",
- "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nSend to Warehouse\nReceive at Warehouse",
+ "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor",
"read_only": 1
},
{
@@ -630,13 +632,21 @@
{
"fieldname": "print_settings_col_break",
"fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval: doc.purpose=='Material Transfer' && !doc.outgoing_stock_entry",
+ "fieldname": "add_to_transit",
+ "fieldtype": "Check",
+ "label": "Add to Transit",
+ "no_copy": 1
}
],
"icon": "fa fa-file-text",
"idx": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-04-23 12:56:52.881752",
+ "modified": "2020-08-11 19:10:07.954981",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 229cf02..30bcccd 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -96,6 +96,11 @@
self.update_quality_inspection()
if self.work_order and self.purpose == "Manufacture":
self.update_so_in_serial_number()
+
+ if self.purpose == 'Material Transfer' and self.add_to_transit:
+ self.set_material_request_transfer_status('In Transit')
+ if self.purpose == 'Material Transfer' and self.outgoing_stock_entry:
+ self.set_material_request_transfer_status('Completed')
def on_cancel(self):
@@ -116,6 +121,11 @@
self.update_quality_inspection()
self.delete_auto_created_batches()
+ if self.purpose == 'Material Transfer' and self.add_to_transit:
+ self.set_material_request_transfer_status('Not Started')
+ if self.purpose == 'Material Transfer' and self.outgoing_stock_entry:
+ self.set_material_request_transfer_status('In Transit')
+
def set_job_card_data(self):
if self.job_card and not self.work_order:
data = frappe.db.get_value('Job Card',
@@ -133,7 +143,7 @@
def validate_purpose(self):
valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer",
"Material Transfer for Manufacture", "Manufacture", "Repack", "Send to Subcontractor",
- "Material Consumption for Manufacture", "Send to Warehouse", "Receive at Warehouse"]
+ "Material Consumption for Manufacture"]
if self.purpose not in valid_purposes:
frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
@@ -199,7 +209,8 @@
item.set(f, item_details.get(f))
if not item.transfer_qty and item.qty:
- item.transfer_qty = item.qty * item.conversion_factor
+ item.transfer_qty = flt(flt(item.qty) * flt(item.conversion_factor),
+ self.precision("transfer_qty", item))
if (self.purpose in ("Material Transfer", "Material Transfer for Manufacture")
and not item.serial_no
@@ -258,10 +269,10 @@
"""perform various (sometimes conditional) validations on warehouse"""
source_mandatory = ["Material Issue", "Material Transfer", "Send to Subcontractor", "Material Transfer for Manufacture",
- "Material Consumption for Manufacture", "Send to Warehouse", "Receive at Warehouse"]
+ "Material Consumption for Manufacture"]
target_mandatory = ["Material Receipt", "Material Transfer", "Send to Subcontractor",
- "Material Transfer for Manufacture", "Send to Warehouse", "Receive at Warehouse"]
+ "Material Transfer for Manufacture"]
validate_for_manufacture = any([d.bom_no for d in self.get("items")])
@@ -809,7 +820,7 @@
def set_items_for_stock_in(self):
self.items = []
- if self.outgoing_stock_entry and self.purpose == 'Receive at Warehouse':
+ if self.outgoing_stock_entry and self.purpose == 'Material Transfer':
doc = frappe.get_doc('Stock Entry', self.outgoing_stock_entry)
if doc.per_transferred == 100:
@@ -1210,13 +1221,25 @@
def validate_with_material_request(self):
for item in self.get("items"):
- if item.material_request:
+ material_request = item.material_request or None
+ material_request_item = item.material_request_item or None
+ if self.purpose == 'Material Transfer' and self.outgoing_stock_entry:
+ parent_se = frappe.get_value("Stock Entry Detail", item.ste_detail, ['material_request','material_request_item'],as_dict=True)
+ if parent_se:
+ material_request = parent_se.material_request
+ material_request_item = parent_se.material_request_item
+
+ if material_request:
mreq_item = frappe.db.get_value("Material Request Item",
- {"name": item.material_request_item, "parent": item.material_request},
+ {"name": material_request_item, "parent": material_request},
["item_code", "warehouse", "idx"], as_dict=True)
- if mreq_item.item_code != item.item_code or \
- mreq_item.warehouse != (item.s_warehouse if self.purpose== "Material Issue" else item.t_warehouse):
- frappe.throw(_("Item or Warehouse for row {0} does not match Material Request").format(item.idx),
+ if mreq_item.item_code != item.item_code:
+ frappe.throw(_("Item for row {0} does not match Material Request").format(item.idx),
+ frappe.MappingMismatchError)
+ elif self.purpose == "Material Transfer" and self.add_to_transit:
+ continue
+ elif mreq_item.warehouse != (item.s_warehouse if self.purpose == "Material Issue" else item.t_warehouse):
+ frappe.throw(_("Warehouse for row {0} does not match Material Request").format(item.idx),
frappe.MappingMismatchError)
def validate_batch(self):
@@ -1284,7 +1307,7 @@
to fullfill Sales Order {2}.").format(item.item_code, sr, sales_order))
def update_transferred_qty(self):
- if self.purpose == 'Receive at Warehouse':
+ if self.purpose == 'Material Transfer' and self.outgoing_stock_entry:
stock_entries = {}
stock_entries_child_list = []
for d in self.items:
@@ -1342,6 +1365,20 @@
'reference_type': reference_type,
'reference_name': reference_name
})
+ def set_material_request_transfer_status(self, status):
+ material_requests = []
+ if self.outgoing_stock_entry:
+ parent_se = frappe.get_value("Stock Entry", self.outgoing_stock_entry, 'add_to_transit')
+
+ for item in self.items:
+ material_request = item.material_request or None
+ if self.purpose == "Material Transfer" and material_request not in material_requests:
+ if self.outgoing_stock_entry and parent_se:
+ material_request = frappe.get_value("Stock Entry Detail", item.ste_detail, 'material_request')
+
+ if material_request and material_request not in material_requests:
+ material_requests.append(material_request)
+ frappe.db.set_value('Material Request', material_request, 'transfer_status', status)
@frappe.whitelist()
def move_sample_to_retention_warehouse(company, items):
@@ -1381,12 +1418,19 @@
@frappe.whitelist()
def make_stock_in_entry(source_name, target_doc=None):
+
def set_missing_values(source, target):
- target.purpose = 'Receive at Warehouse'
target.set_stock_entry_type()
def update_item(source_doc, target_doc, source_parent):
target_doc.t_warehouse = ''
+
+ if source_doc.material_request_item and source_doc.material_request :
+ add_to_transit = frappe.db.get_value('Stock Entry', source_name, 'add_to_transit')
+ if add_to_transit:
+ warehouse = frappe.get_value('Material Request Item', source_doc.material_request_item, 'warehouse')
+ target_doc.t_warehouse = warehouse
+
target_doc.s_warehouse = source_doc.t_warehouse
target_doc.qty = source_doc.qty - source_doc.transferred_qty
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 8e25804..d98870d 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -737,34 +737,6 @@
self.assertEqual(se.get("items")[0].allow_zero_valuation_rate, 1)
self.assertEqual(se.get("items")[0].amount, 0)
- def test_goods_in_transit(self):
- from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
- warehouse = "_Test Warehouse FG 1 - _TC"
-
- if not frappe.db.exists('Warehouse', warehouse):
- create_warehouse("_Test Warehouse FG 1")
-
- outward_entry = make_stock_entry(item_code="_Test Item",
- purpose="Send to Warehouse",
- source="_Test Warehouse - _TC",
- target="_Test Warehouse 1 - _TC", qty=50, basic_rate=100)
-
- inward_entry1 = make_stock_in_entry(outward_entry.name)
- inward_entry1.items[0].t_warehouse = warehouse
- inward_entry1.items[0].qty = 25
- inward_entry1.submit()
-
- doc = frappe.get_doc('Stock Entry', outward_entry.name)
- self.assertEqual(doc.per_transferred, 50)
-
- inward_entry2 = make_stock_in_entry(outward_entry.name)
- inward_entry2.items[0].t_warehouse = warehouse
- inward_entry2.items[0].qty = 25
- inward_entry2.submit()
-
- doc = frappe.get_doc('Stock Entry', outward_entry.name)
- self.assertEqual(doc.per_transferred, 100)
-
def test_gle_for_opening_stock_entry(self):
mr = make_stock_entry(item_code="_Test Item", target="Stores - TCP1", company="_Test Company with perpetual inventory",qty=50, basic_rate=100, expense_account="Stock Adjustment - TCP1", is_opening="Yes", do_not_save=True)
diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json
index edee3c7..0f2b55e 100644
--- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json
+++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json
@@ -1,156 +1,83 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
+ "actions": [],
"autoname": "Prompt",
- "beta": 0,
"creation": "2019-03-13 16:23:46.636769",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
- "document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
+ "field_order": [
+ "purpose"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "Material Issue",
- "fetch_if_empty": 0,
"fieldname": "purpose",
"fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "Purpose",
- "length": 0,
- "no_copy": 0,
- "options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nSend to Warehouse\nReceive at Warehouse",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
+ "options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor",
"reqd": 1,
- "search_index": 0,
- "set_only_once": 1,
- "translatable": 0,
- "unique": 0
+ "set_only_once": 1
}
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-03-26 12:02:42.144377",
+ "links": [],
+ "modified": "2020-08-10 23:24:37.160817",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry Type",
- "name_case": "",
"owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
},
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Manufacturing Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
},
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Stock Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
},
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Stock User",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
}
],
"quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "ASC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/templates/generators/item/item_image.html b/erpnext/templates/generators/item/item_image.html
index 0dd4c35..5d46a45 100644
--- a/erpnext/templates/generators/item/item_image.html
+++ b/erpnext/templates/generators/item/item_image.html
@@ -23,7 +23,7 @@
})
</script>
{% else %}
-{{ product_image(website_image or image or 'no-image.jpg') }}
+{{ product_image(website_image or image or 'no-image.jpg', alt=website_image_alt or item_name) }}
{% endif %}
<!-- Simple image preview -->
diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html
index 3c82e90..ea6b00f 100644
--- a/erpnext/templates/includes/macros.html
+++ b/erpnext/templates/includes/macros.html
@@ -7,9 +7,9 @@
</div>
{% endmacro %}
-{% macro product_image(website_image, css_class="") %}
+{% macro product_image(website_image, css_class="", alt="") %}
<div class="border text-center rounded h-100 {{ css_class }}" style="overflow: hidden;">
- <img itemprop="image" class="website-image h-100 w-100" src="{{ frappe.utils.quoted(website_image or 'no-image.jpg') | abs_url }}">
+ <img itemprop="image" class="website-image h-100 w-100" alt="{{ alt }}" src="{{ frappe.utils.quoted(website_image or 'no-image.jpg') | abs_url }}">
</div>
{% endmacro %}
diff --git a/erpnext/www/support/index.html b/erpnext/www/support/index.html
index 93da503..12b4c2c 100644
--- a/erpnext/www/support/index.html
+++ b/erpnext/www/support/index.html
@@ -9,6 +9,33 @@
<p class="hero-subtitle">{{ greeting_subtitle }}</p>
{% endif %}
</div>
+ <div class="search-container">
+ <div class="website-search" id="search-container">
+ <div class="dropdown">
+ <div class="search-icon">
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor" stroke-width="2" stroke-linecap="round"
+ stroke-linejoin="round"
+ class="feather feather-search">
+ <circle cx="11" cy="11" r="8"></circle>
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
+ </svg>
+ </div>
+ <input type="search" class="form-control" placeholder="Search the docs (Press ? to focus)" />
+ <div class="overflow-hidden shadow dropdown-menu w-100">
+ </div>
+ </div>
+ </div>
+ <button class="navbar-toggler" type="button"
+ data-toggle="collapse"
+ data-target="#navbarSupportedContent"
+ aria-controls="navbarSupportedContent"
+ aria-expanded="false"
+ aria-label="Toggle navigation">
+ <span class="navbar-toggler-icon"></span>
+ </button>
+ </div>
</div>
</section>
@@ -54,5 +81,21 @@
</div>
</section>
{% endif %}
+{% endblock %}
-{% endblock %}
\ No newline at end of file
+{%- block script -%}
+<script>
+ frappe.ready(() => {
+ frappe.setup_search('#search-container', 'kb');
+ });
+</script>
+{%- endblock -%}
+
+{%- block style -%}
+<style>
+ .search-container {
+ margin-top: 1.2rem;
+ max-width: 500px;
+ }
+</style>
+{%- endblock -%}