Merge pull request #16633 from pratu16x7/explore-2.0
feat(modules): Add getting started and help desk, merge settings
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 3802fea..abd3be6 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
-__version__ = '11.1.4'
+__version__ = '11.1.6'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index 906dfbe..e305652 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -60,7 +60,7 @@
deferred_account = "deferred_revenue_account" if doc.doctype=="Sales Invoice" else "deferred_expense_account"
last_gl_entry, skip = False, False
- booking_end_date = getdate(add_days(today(), -1)) if not end_date else end_date
+ booking_end_date = getdate(add_days(today(), -1) if not end_date else end_date)
if booking_end_date < item.service_start_date or \
(item.service_stop_date and booking_end_date.month > item.service_stop_date.month):
return None, None, None, True
@@ -71,7 +71,7 @@
last_gl_entry = True
booking_end_date = item.service_stop_date
- booking_start_date = getdate(add_months(today(), -1)) if not start_date else start_date
+ booking_start_date = getdate(add_months(today(), -1) if not start_date else start_date)
booking_start_date = booking_start_date \
if booking_start_date > item.service_start_date else item.service_start_date
@@ -113,7 +113,6 @@
group by voucher_detail_no
'''.format(total_credit_debit, total_credit_debit_currency),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
-
already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
if account_currency==doc.company_currency:
@@ -128,15 +127,19 @@
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
# start_date: 1st of the last month or the start date
# end_date: end_date or today-1
+ enable_check = "enable_deferred_revenue" \
+ if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
gl_entries = []
for item in doc.get('items'):
+ if not item.get(enable_check): continue
+
skip = False
last_gl_entry, booking_start_date, booking_end_date, skip = \
get_booking_dates(doc, item, start_date, end_date)
if skip: continue
- total_days = date_diff(item.service_end_date, item.service_start_date)
+ total_days = date_diff(item.service_end_date, item.service_start_date) + 1
total_booking_days = date_diff(booking_end_date, booking_start_date) + 1
account_currency = get_account_currency(item.expense_account)
@@ -175,6 +178,10 @@
'project': project
}, account_currency)
)
-
if gl_entries:
- make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
+ try:
+ make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
+ frappe.db.commit()
+ except:
+ frappe.db.rollback()
+ frappe.log_error(message = frappe.get_traceback(), title = _("Error while processing deferred accounting for {0}").format(doc.name))
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 655926b..696736d 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -171,7 +171,7 @@
if not frappe.db.exists(self.party_type, self.party):
frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
- if self.party_account:
+ if self.party_account and self.party_type in ("Customer", "Supplier"):
self.validate_account_type(self.party_account,
[erpnext.get_party_account_type(self.party_type)])
@@ -689,7 +689,7 @@
account_currency = get_account_currency(party_account)
account_balance = get_balance_on(party_account, date, cost_center=cost_center)
- _party_name = "title" if party_type == "Student" else party_type.lower() + "_name"
+ _party_name = "title" if party_type in ("Student", "Shareholder") else party_type.lower() + "_name"
party_name = frappe.db.get_value(party_type, party, _party_name)
party_balance = get_balance_on(party_type=party_type, party=party, cost_center=cost_center)
if party_type in ["Customer", "Supplier"]:
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 8c3deaf..be66332 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -101,7 +101,7 @@
frappe.db.get_value(filters.party_type, filters.party[0], "default_currency"))
filters["account_currency"] = account_currency or filters.company_currency
- if filters.account_currency != filters.company_currency:
+ if filters.account_currency != filters.company_currency and not filters.presentation_currency:
filters.presentation_currency = filters.account_currency
return filters
diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py
index 5d24096..e33b90d 100644
--- a/erpnext/accounts/report/purchase_register/purchase_register.py
+++ b/erpnext/accounts/report/purchase_register/purchase_register.py
@@ -193,7 +193,7 @@
pi_items = frappe.db.sql("""
select parent, purchase_order, purchase_receipt, po_detail, project
from `tabPurchase Invoice Item`
- where parent in (%s) and (ifnull(purchase_order, '') != '' or ifnull(purchase_receipt, '') != '')
+ where parent in (%s)
""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
invoice_po_pr_map = {}
diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py
index a8ae94f..8a39744 100644
--- a/erpnext/accounts/report/utils.py
+++ b/erpnext/accounts/report/utils.py
@@ -104,7 +104,7 @@
credit_in_account_currency = flt(entry['credit_in_account_currency'])
account_currency = entry['account_currency']
- if account_currency != presentation_currency or (account_currency == presentation_currency and not is_p_or_l_account(account)):
+ if account_currency != presentation_currency:
value = debit or credit
date = currency_info['report_date'] if not is_p_or_l_account(account) else entry['posting_date']
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index a9fcff8..1c9d977 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -615,7 +615,7 @@
return held_invoices
-def get_outstanding_invoices(party_type, party, account, condition=None, limit=1000):
+def get_outstanding_invoices(party_type, party, account, condition=None, limit=None):
outstanding_invoices = []
precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2
@@ -628,7 +628,7 @@
invoice = 'Sales Invoice' if erpnext.get_party_account_type(party_type) == 'Receivable' else 'Purchase Invoice'
held_invoices = get_held_invoices(party_type, party)
- limit_cond = "limit %s" % (limit or 1000)
+ limit_cond = "limit %s" % limit if limit else ""
invoice_list = frappe.db.sql("""
select
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index 6b3c3cc73..bbe92f6 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -1812,8 +1812,9 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_from": "company.default_finance_book",
"fieldname": "default_finance_book",
- "fieldtype": "Read Only",
+ "fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -1824,12 +1825,12 @@
"label": "Default Finance Book",
"length": 0,
"no_copy": 0,
- "options": "company.default_finance_book",
+ "options": "Finance Book",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
- "read_only": 0,
+ "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@@ -1882,7 +1883,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2019-01-15 16:12:48.314196",
+ "modified": "2019-02-12 11:29:01.747819",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index e72f59a..e79a2ac 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -459,7 +459,7 @@
items_dict = {
rm_item_code: {
"item_name": rm_item_data["item_name"],
- "description": item_wh[rm_item_code].get('description'),
+ "description": item_wh.get(rm_item_code, {}).get('description', ""),
'qty': rm_item_data["qty"],
'from_warehouse': rm_item_data["warehouse"],
'stock_uom': rm_item_data["stock_uom"],
diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.json b/erpnext/buying/report/purchase_analytics/purchase_analytics.json
index 996e3ee..7ce779d 100644
--- a/erpnext/buying/report/purchase_analytics/purchase_analytics.json
+++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.json
@@ -1,12 +1,13 @@
{
- "add_total_row": 0,
+ "add_total_row": 1,
"creation": "2018-10-05 16:08:24.156448",
+ "disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
- "modified": "2018-10-05 16:08:33.272201",
+ "modified": "2019-02-12 14:32:29.107109",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Analytics",
diff --git a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
index bb4f62a..1edc102 100644
--- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
+++ b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
@@ -28,7 +28,7 @@
if not frappe.get_value("Custom Field",{"name":i[0]}) or not frappe.get_value("Custom Field",{"name":i[1]}):
create_custom_field_id_and_check_status = True
- break;
+ break
if create_custom_field_id_and_check_status:
diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json
index df56918..26564a3 100644
--- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json
+++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
@@ -55,7 +56,7 @@
"collapsible": 0,
"columns": 0,
"fieldname": "item_code",
- "fieldtype": "Read Only",
+ "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -71,7 +72,7 @@
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
- "read_only": 0,
+ "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@@ -707,7 +708,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-08-08 13:00:06.260997",
+ "modified": "2019-02-12 11:37:18.713344",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Clinical Procedure Template",
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js
index af7205b..77a2bbc 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.js
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.js
@@ -19,7 +19,6 @@
filters: {
"root_type": "Asset",
"is_group": 0,
- "account_type": "Payable",
"company": frm.doc.company
}
};
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json
index 83afa8c..76e43d6 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.json
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.json
@@ -195,7 +195,7 @@
"columns": 0,
"fetch_from": "employee.branch",
"fieldname": "branch",
- "fieldtype": "Read Only",
+ "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -208,11 +208,11 @@
"no_copy": 0,
"oldfieldname": "branch",
"oldfieldtype": "Link",
- "options": "",
+ "options": "Branch",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
- "read_only": 0,
+ "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@@ -1906,7 +1906,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2019-01-30 11:28:11.774739",
+ "modified": "2019-02-12 11:24:20.848207",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",
diff --git a/erpnext/hr/report/salary_register/salary_register.py b/erpnext/hr/report/salary_register/salary_register.py
index 237c5e2..0c42150 100644
--- a/erpnext/hr/report/salary_register/salary_register.py
+++ b/erpnext/hr/report/salary_register/salary_register.py
@@ -19,12 +19,12 @@
data = []
for ss in salary_slips:
row = [ss.name, ss.employee, ss.employee_name, ss.branch, ss.department, ss.designation,
- ss.company, ss.start_date, ss.end_date, ss.leave_withut_pay, ss.payment_days]
+ ss.company, ss.start_date, ss.end_date, ss.leave_without_pay, ss.payment_days]
if not ss.branch == None:columns[3] = columns[3].replace('-1','120')
if not ss.department == None: columns[4] = columns[4].replace('-1','120')
if not ss.designation == None: columns[5] = columns[5].replace('-1','120')
- if not ss.leave_withut_pay == None: columns[9] = columns[9].replace('-1','130')
+ if not ss.leave_without_pay == None: columns[9] = columns[9].replace('-1','130')
for e in earning_types:
@@ -115,4 +115,4 @@
ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, [])
ss_ded_map[d.parent][d.salary_component] = flt(d.amount)
- return ss_ded_map
\ No newline at end of file
+ return ss_ded_map
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 50d8ed2..f439588 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -276,8 +276,8 @@
item_dict[(d.item_code, d.material_request_item, d.warehouse)] = item_details
else:
item_details.update({
- "qty":flt(item_dict.get((d.item_code, d.sales_order, d.warehouse),{})
- .get("qty")) + flt(d.planned_qty)
+ "qty": flt(item_dict.get((d.item_code, d.sales_order, d.warehouse),{})
+ .get("qty")) + (flt(d.planned_qty) - flt(d.ordered_qty))
})
item_dict[(d.item_code, d.sales_order, d.warehouse)] = item_details
diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
index b6f7d01..be016ad 100644
--- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
+++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
@@ -13,16 +13,16 @@
data = get_bom_stock(filters)
qty_to_make = filters.get("qty_to_make")
- for rows in data:
- item_map = get_item_details(rows[0])
- reqd_qty = qty_to_make * rows[3]
- last_pur_price = frappe.db.get_value("Item", rows[0], "last_purchase_rate")
- if rows[4] > 0:
- diff_qty = rows[4] - reqd_qty
- summ_data.append([rows[0], rows[1], item_map[rows[0]]["manufacturer"], item_map[rows[0]]["manufacturer_part_no"], rows[3], rows[4], reqd_qty, diff_qty, last_pur_price])
+ for row in data:
+ item_map = get_item_details(row.item_code)
+ reqd_qty = qty_to_make * row.actual_qty
+ last_pur_price = frappe.db.get_value("Item", row.item_code, "last_purchase_rate")
+ if row.to_build > 0:
+ diff_qty = row.to_build - reqd_qty
+ summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, row.to_build, reqd_qty, diff_qty, last_pur_price])
else:
diff_qty = 0 - reqd_qty
- summ_data.append([rows[0], rows[1], item_map[rows[0]]["manufacturer"], item_map[rows[0]]["manufacturer_part_no"], rows[3], "0.000", reqd_qty, diff_qty, last_pur_price])
+ summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, "0.000", reqd_qty, diff_qty, last_pur_price])
return columns, summ_data
@@ -72,8 +72,8 @@
bom_item.item_code,
bom_item.description,
bom_item.{qty_field},
- sum(ledger.actual_qty) as actual_qty,
- sum(FLOOR(ledger.actual_qty / bom_item.{qty_field}))as to_build
+ ifnull(sum(ledger.actual_qty), 0) as actual_qty,
+ ifnull(sum(FLOOR(ledger.actual_qty / bom_item.{qty_field})), 0) as to_build
FROM
{table} AS bom_item
LEFT JOIN `tabBin` AS ledger
@@ -83,7 +83,7 @@
WHERE
bom_item.parent = '{bom}' and bom_item.parenttype='BOM'
- GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom))
+ GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
def get_item_details(item_code):
items = frappe.db.sql("""select it.item_group, it.item_name, it.stock_uom, it.name, it.brand, it.description, it.manufacturer_part_no, it.manufacturer from tabItem it where it.item_code = %s""", item_code, as_dict=1)
diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.json b/erpnext/manufacturing/report/production_analytics/production_analytics.json
index 023e0a8..946cd33 100644
--- a/erpnext/manufacturing/report/production_analytics/production_analytics.json
+++ b/erpnext/manufacturing/report/production_analytics/production_analytics.json
@@ -1,13 +1,14 @@
{
- "add_total_row": 0,
+ "add_total_row": 1,
"creation": "2018-10-11 19:28:37.085066",
+ "disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "",
- "modified": "2018-10-11 19:28:37.085066",
+ "modified": "2019-02-12 14:32:16.392521",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Analytics",
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 8ab6153..0826ec6 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -581,4 +581,5 @@
erpnext.patches.v11_0.renamed_from_to_fields_in_project
erpnext.patches.v11_0.add_permissions_in_gst_settings
erpnext.patches.v11_1.setup_guardian_role
+execute:frappe.delete_doc('DocType', 'Notification Control')
erpnext.patches.v12_0.move_item_tax_to_item_tax_template
diff --git a/erpnext/patches/v11_0/create_department_records_for_each_company.py b/erpnext/patches/v11_0/create_department_records_for_each_company.py
index 1257f19..b5a7bd9 100644
--- a/erpnext/patches/v11_0/create_department_records_for_each_company.py
+++ b/erpnext/patches/v11_0/create_department_records_for_each_company.py
@@ -27,8 +27,10 @@
for company in companies:
copy_doc = frappe.copy_doc(department_doc)
copy_doc.update({"company": company.name})
- copy_doc.insert()
-
+ try:
+ copy_doc.insert()
+ except frappe.DuplicateEntryError:
+ pass
# append list of new department for each company
comp_dict[company.name][department.name] = copy_doc.name
diff --git a/erpnext/patches/v11_0/update_total_qty_field.py b/erpnext/patches/v11_0/update_total_qty_field.py
index fcb76af..992454a 100644
--- a/erpnext/patches/v11_0/update_total_qty_field.py
+++ b/erpnext/patches/v11_0/update_total_qty_field.py
@@ -19,9 +19,10 @@
SELECT
parent, SUM(qty) as qty
FROM
- `tab%s Item`
+ `tab{0} Item`
+ where parenttype = '{0}'
GROUP BY parent
- ''' % (doctype), as_dict = True)
+ '''.format(doctype), as_dict = True)
# Query to update total_qty might become too big, Update in batches
# batch_size is chosen arbitrarily, Don't try too hard to reason about it
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 3b42f6a6..e3fd1a8 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -66,11 +66,11 @@
def validate(self):
self.validate_project_name()
- self.validate_dates()
self.validate_weights()
self.sync_tasks()
self.tasks = []
self.load_tasks()
+ self.validate_dates()
self.send_welcome_email()
self.update_percent_complete()
@@ -79,6 +79,24 @@
frappe.throw(_("Project {0} already exists").format(frappe.safe_decode(self.project_name)))
def validate_dates(self):
+ if self.tasks:
+ for d in self.tasks:
+ if self.expected_start_date:
+ if d.start_date and getdate(d.start_date) < getdate(self.expected_start_date):
+ frappe.throw(_("Start date of task <b>{0}</b> cannot be less than <b>{1}</b> expected start date <b>{2}</b>")
+ .format(d.title, self.name, self.expected_start_date))
+ if d.end_date and getdate(d.end_date) < getdate(self.expected_start_date):
+ frappe.throw(_("End date of task <b>{0}</b> cannot be less than <b>{1}</b> expected start date <b>{2}</b>")
+ .format(d.title, self.name, self.expected_start_date))
+
+ if self.expected_end_date:
+ if d.start_date and getdate(d.start_date) > getdate(self.expected_end_date):
+ frappe.throw(_("Start date of task <b>{0}</b> cannot be greater than <b>{1}</b> expected end date <b>{2}</b>")
+ .format(d.title, self.name, self.expected_end_date))
+ if d.end_date and getdate(d.end_date) > getdate(self.expected_end_date):
+ frappe.throw(_("End date of task <b>{0}</b> cannot be greater than <b>{1}</b> expected end date <b>{2}</b>")
+ .format(d.title, self.name, self.expected_end_date))
+
if self.expected_start_date and self.expected_end_date:
if getdate(self.expected_end_date) < getdate(self.expected_start_date):
frappe.throw(_("Expected End Date can not be less than Expected Start Date"))
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index 1fcb3c6..6613eb1 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -44,12 +44,6 @@
if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date):
frappe.throw(_("'Actual Start Date' can not be greater than 'Actual End Date'"))
- if(self.project):
- if frappe.db.exists("Project", self.project):
- expected_end_date = frappe.db.get_value("Project", self.project, "expected_end_date")
- if self.exp_end_date and expected_end_date and getdate(self.exp_end_date) > getdate(expected_end_date) :
- frappe.throw(_("Expected end date cannot be after Project: <b>'{0}'</b> Expected end date").format(self.project), EndDateCannotBeGreaterThanProjectEndDateError)
-
def validate_status(self):
if self.status!=self.get_db_value("status") and self.status == "Closed":
for d in self.depends_on:
diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py
index 6fb5412..9971946 100644
--- a/erpnext/projects/doctype/task/test_task.py
+++ b/erpnext/projects/doctype/task/test_task.py
@@ -5,7 +5,7 @@
import unittest
from frappe.utils import getdate, nowdate, add_days
-from erpnext.projects.doctype.task.task import CircularReferenceError, EndDateCannotBeGreaterThanProjectEndDateError
+from erpnext.projects.doctype.task.task import CircularReferenceError
class TestTask(unittest.TestCase):
def test_circular_reference(self):
@@ -97,15 +97,6 @@
self.assertEqual(frappe.db.get_value("Task", task.name, "status"), "Overdue")
- def test_end_date_validation(self):
- task_end = create_task("Testing_Enddate_validation", add_days(nowdate(), 35), add_days(nowdate(), 45), save=False)
- pro = frappe.get_doc("Project", task_end.project)
- pro.expected_end_date = add_days(nowdate(), 40)
- pro.save()
- self.assertRaises(EndDateCannotBeGreaterThanProjectEndDateError, task_end.save)
-
-
-
def create_task(subject, start=None, end=None, depends_on=None, project=None, save=True):
if not frappe.db.exists("Task", subject):
task = frappe.new_doc('Task')
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 8692440..3757315 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -18,7 +18,7 @@
return{
filters: {
'project': child.project,
- 'status': ["!=", "Closed"]
+ 'status': ["!=", "Cancelled"]
}
}
}
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index ff90d13..5da7e73 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -276,13 +276,11 @@
scan_barcode: function() {
let scan_barcode_field = this.frm.fields_dict["scan_barcode"];
- let show_description = function(idx, item_code, exist=null) {
- if(exist) {
- scan_barcode_field.set_new_description(__('Row : ') + idx + ' ' +
- item_code + __(' Qty increased by 1'));
+ let show_description = function(idx, exist = null) {
+ if (exist) {
+ scan_barcode_field.set_new_description(__('Row #{0}: Qty increased by 1', [idx]));
} else {
- scan_barcode_field.set_new_description(__('New row : ') + idx + ' ' +
- item_code + __(' Created'));
+ scan_barcode_field.set_new_description(__('Row #{0}: Item added', [idx]));
}
}
@@ -291,38 +289,39 @@
method: "erpnext.selling.page.point_of_sale.point_of_sale.search_serial_or_batch_or_barcode_number",
args: { search_value: this.frm.doc.scan_barcode }
}).then(r => {
-
- if(r && r.message && r.message.item_code) {
- let child = "";
- let add_row_index = -1;
- let cur_grid= this.frm.fields_dict["items"].grid;
-
- this.frm.doc.items.map(d => {
- if(d.item_code==r.message.item_code){
- add_row_index = d.idx;
- return;
- } else if(!d.item_code && add_row_index==-1) {
- add_row_index = d.idx;
- }
- });
-
- if(add_row_index == -1) {
- child = frappe.model.add_child(this.frm.doc, cur_grid.doctype, "items", add_row_index);
- } else {
- child = cur_grid.get_grid_row(add_row_index-1).doc;
- }
- show_description(child.idx, r.message.item_code, child.item_code);
-
- frappe.model.set_value(child.doctype, child.name, {
- "item_code": r.message.item_code,
- "qty": (child.qty || 0) + 1
- });
+ const data = r && r.message;
+ if (!data) {
+ scan_barcode_field.set_new_description(__('Cannot find Item with this barcode'));
+ return;
}
- else{
- scan_barcode_field.set_new_description(this.frm.doc.scan_barcode +__(' does not exist!'));
+
+ let cur_grid = this.frm.fields_dict.items.grid;
+
+ let row_to_modify = null;
+ const existing_item_row = this.frm.doc.items.find(d => d.item_code === data.item_code);
+ const blank_item_row = this.frm.doc.items.find(d => !d.item_code);
+
+ if (existing_item_row) {
+ row_to_modify = existing_item_row;
+ } else if (blank_item_row) {
+ row_to_modify = blank_item_row;
}
+
+ if (!row_to_modify) {
+ // add new row
+ row_to_modify = frappe.model.add_child(this.frm.doc, cur_grid.doctype, 'items');
+ }
+
+ show_description(row_to_modify.idx, row_to_modify.item_code);
+
+ frappe.model.set_value(row_to_modify.doctype, row_to_modify.name, {
+ item_code: data.item_code,
+ qty: (row_to_modify.qty || 0) + 1
+ });
+
+ this.frm.refresh_field('items');
});
- scan_barcode_field.set_value("");
+ scan_barcode_field.set_value('');
}
return false;
},
diff --git a/erpnext/regional/germany/__init__.py b/erpnext/regional/germany/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/germany/__init__.py
diff --git a/erpnext/regional/germany/address_template.html b/erpnext/regional/germany/address_template.html
new file mode 100644
index 0000000..0df7867
--- /dev/null
+++ b/erpnext/regional/germany/address_template.html
@@ -0,0 +1,8 @@
+{{ address_line1 }}<br>
+{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}
+{{ pincode }} {{ city }}<br>
+{% if country %}{{ country }}<br>{% endif -%}
+<br>
+{% if phone %}Tel: {{ phone }}<br>{% endif -%}
+{% if fax %}Fax: {{ fax }}<br>{% endif -%}
+{% if email_id %}E-Mail: {{ email_id }}<br>{% endif -%}
diff --git a/erpnext/regional/germany/setup.py b/erpnext/regional/germany/setup.py
new file mode 100644
index 0000000..a547136
--- /dev/null
+++ b/erpnext/regional/germany/setup.py
@@ -0,0 +1,31 @@
+import os
+import frappe
+
+
+def setup(company=None, patch=True):
+ if not patch:
+ update_address_template()
+
+
+def update_address_template():
+ """
+ Read address template from file. Update existing Address Template or create a
+ new one.
+ """
+ dir_name = os.path.dirname(__file__)
+ template_path = os.path.join(dir_name, 'address_template.html')
+
+ with open(template_path, 'r') as template_file:
+ template_html = template_file.read()
+
+ address_template = frappe.db.get_value('Address Template', 'Germany')
+
+ if address_template:
+ frappe.db.set_value('Address Template', 'Germany', 'template', template_html)
+ else:
+ # make new html template for Germany
+ frappe.get_doc(dict(
+ doctype='Address Template',
+ country='Germany',
+ template=template_html
+ )).insert()
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index a6d723a..c5498c7 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -345,13 +345,14 @@
if company and tds_account:
accounts = [dict(company=company, account=tds_account)]
- fiscal_year = get_fiscal_year(today(), company=accounts[0].get('company'))[0]
+ fiscal_year = get_fiscal_year(today(), company=company)[0]
docs = get_tds_details(accounts, fiscal_year)
for d in docs:
try:
doc = frappe.get_doc(d)
doc.flags.ignore_permissions = True
+ doc.flags.ignore_mandatory = True
doc.insert()
except frappe.DuplicateEntryError:
doc = frappe.get_doc("Tax Withholding Category", d.get("name"))
diff --git a/erpnext/selling/report/address_and_contacts/address_and_contacts.py b/erpnext/selling/report/address_and_contacts/address_and_contacts.py
index a9e4303..eb242d0 100644
--- a/erpnext/selling/report/address_and_contacts/address_and_contacts.py
+++ b/erpnext/selling/report/address_and_contacts/address_and_contacts.py
@@ -102,7 +102,8 @@
records = frappe.get_list(doctype, filters=filters, fields=fields, as_list=True)
for d in records:
details = party_details.get(d[0])
- details.setdefault(frappe.scrub(doctype), []).append(d[1:])
+ if details:
+ details.setdefault(frappe.scrub(doctype), []).append(d[1:])
return party_details
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.json b/erpnext/selling/report/sales_analytics/sales_analytics.json
index 5c95f28..bd82302 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.json
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.json
@@ -1,12 +1,13 @@
{
- "add_total_row": 0,
+ "add_total_row": 1,
"creation": "2018-09-21 12:46:29.451048",
+ "disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
- "modified": "2018-09-21 12:46:29.451048",
+ "modified": "2019-02-12 14:30:40.043652",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Analytics",
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index a0d94bf..5669552 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -703,15 +703,14 @@
frappe.db.get_single_value('Item Variant Settings', 'do_not_update_variants'):
return
if self.has_variants:
- updated = []
variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name})
- for d in variants:
- variant = frappe.get_doc("Item", d)
- copy_attributes_to_variant(self, variant)
- variant.save()
- updated.append(d.item_code)
- if updated:
- frappe.msgprint(_("Item Variants {0} updated").format(", ".join(updated)))
+ if variants:
+ if len(variants) <= 30:
+ update_variants(variants, self, publish_progress=False)
+ frappe.msgprint(_("Item Variants updated"))
+ else:
+ frappe.enqueue("erpnext.stock.doctype.item.item.update_variants",
+ variants=variants, template=self, now=frappe.flags.in_test, timeout=600)
def validate_has_variants(self):
if not self.has_variants and frappe.db.get_value("Item", self.name, "has_variants"):
@@ -1000,3 +999,13 @@
return frappe.get_all("Item Attribute Value", fields = ["attribute_value"],
filters = {'parent': parent, 'attribute_value': ("like", "%%%s%%" % attribute_value)})
+
+def update_variants(variants, template, publish_progress=True):
+ count=0
+ for d in variants:
+ variant = frappe.get_doc("Item", d)
+ copy_attributes_to_variant(template, variant)
+ variant.save()
+ count+=1
+ if publish_progress:
+ frappe.publish_progress(count*100/len(variants), title = _("Updating Variants..."))
\ No newline at end of file
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 34a86c9..7a199a4 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -81,9 +81,9 @@
def set_title(self):
'''Set title as comma separated list of items'''
- items = ', '.join([d.item_name for d in self.items][:4])
-
- self.title = _('{0} for {1}'.format(self.material_request_type, items))[:100]
+ if not self.title:
+ items = ', '.join([d.item_name for d in self.items][:3])
+ self.title = _('{0} Request for {1}').format(self.material_request_type, items)[:100]
def on_submit(self):
# frappe.db.set(self, 'status', 'Submitted')
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 5f0b80e..3012d0b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -297,8 +297,9 @@
total_completed_qty = flt(self.fg_completed_qty) + flt(prod_order.produced_qty)
completed_qty = d.completed_qty + (allowance_percentage/100 * d.completed_qty)
if total_completed_qty > flt(completed_qty):
- frappe.throw(_("Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order # {3}. Please update operation status via Time Logs")
- .format(d.idx, d.operation, total_completed_qty, self.work_order), OperationsNotCompleteError)
+ job_card = frappe.db.get_value('Job Card', {'operation_id': d.name}, 'name')
+ frappe.throw(_("Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order # {3}. Please update operation status via Job Card # {4}")
+ .format(d.idx, d.operation, total_completed_qty, self.work_order, job_card), OperationsNotCompleteError)
def check_duplicate_entry_for_work_order(self):
other_ste = [t[0] for t in frappe.db.get_values("Stock Entry", {
@@ -918,6 +919,11 @@
filters={'parent': self.work_order, 'item_code': item_code},
fields=["required_qty", "consumed_qty"]
)
+ if not req_items:
+ frappe.msgprint(_("Did not found transfered item {0} in Work Order {1}, the item not added in Stock Entry")
+ .format(item_code, self.work_order))
+ continue
+
req_qty = flt(req_items[0].required_qty)
req_qty_each = flt(req_qty / manufacturing_qty)
consumed_qty = flt(req_items[0].consumed_qty)
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index eb60ce5..a00d279 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -259,6 +259,7 @@
def submit(self):
if len(self.items) > 100:
+ msgprint(_("The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Draft stage"))
self.queue_action('submit')
else:
self._submit()
diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py
index ef63740..da97bc6 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.py
+++ b/erpnext/stock/doctype/warehouse/warehouse.py
@@ -157,6 +157,8 @@
# return warehouses
for wh in warehouses:
wh["balance"] = get_stock_value_from_bin(warehouse=wh.value)
+ if company:
+ wh["company_currency"] = frappe.db.get_value('Company', company, 'default_currency')
return warehouses
@frappe.whitelist()
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 3051d31..4561592 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -16,6 +16,9 @@
from six import string_types, iteritems
+sales_doctypes = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']
+purchase_doctypes = ['Material Request', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
+
@frappe.whitelist()
def get_item_details(args):
"""
@@ -232,7 +235,7 @@
#Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master
if not args.uom:
- if args.get('doctype') in ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']:
+ if args.get('doctype') in sales_doctypes:
args.uom = item.sales_uom if item.sales_uom else item.stock_uom
elif (args.get('doctype') in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']) or \
(args.get('doctype') == 'Material Request' and args.get('material_request_type') == 'Purchase'):
@@ -283,14 +286,15 @@
out.conversion_factor = 1.0
else:
out.conversion_factor = args.conversion_factor or \
- get_conversion_factor(item.item_code, args.uom).get("conversion_factor")
+ get_conversion_factor(item.name, args.uom).get("conversion_factor")
args.conversion_factor = out.conversion_factor
out.stock_qty = out.qty * out.conversion_factor
# calculate last purchase rate
- from erpnext.buying.doctype.purchase_order.purchase_order import item_last_purchase_rate
- out.last_purchase_rate = item_last_purchase_rate(args.name, args.conversion_rate, item.item_code, out.conversion_factor)
+ if args.get('doctype') in purchase_doctypes:
+ from erpnext.buying.doctype.purchase_order.purchase_order import item_last_purchase_rate
+ out.last_purchase_rate = item_last_purchase_rate(args.name, args.conversion_rate, item.name, out.conversion_factor)
# if default specified in item is for another company, fetch from company
for d in [
diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.json b/erpnext/stock/report/stock_analytics/stock_analytics.json
index efd5e99..7a2ed2b 100644
--- a/erpnext/stock/report/stock_analytics/stock_analytics.json
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.json
@@ -1,12 +1,13 @@
{
- "add_total_row": 0,
+ "add_total_row": 1,
"creation": "2018-10-08 12:11:32.133020",
+ "disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
- "modified": "2018-10-08 12:18:42.834270",
+ "modified": "2019-02-12 14:32:22.874082",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Analytics",
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index 14b1852..5c443d3 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -216,20 +216,28 @@
if not items:
items = list(set([d.item_code for d in sle]))
- if items:
- cf_field = cf_join = ""
- if filters.get("include_uom"):
- cf_field = ", ucd.`conversion_factor`"
- cf_join = "LEFT JOIN `tabUOM Conversion Detail` ucd ON ucd.`parent`=item.`name` AND ucd.`uom`=%(include_uom)s"
+ if not items:
+ return item_details
- for item in frappe.db.sql("""
- SELECT item.`name`, item.`item_name`, item.`description`, item.`item_group`, item.`brand`, item.`stock_uom` {cf_field}
- FROM `tabItem` item
+ cf_field = cf_join = ""
+ if filters.get("include_uom"):
+ cf_field = ", ucd.conversion_factor"
+ cf_join = "left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom='%s'" \
+ % frappe.db.escape(filters.get("include_uom"))
+
+ item_codes = ', '.join(['"' + frappe.db.escape(i, percent=False) + '"' for i in items])
+ res = frappe.db.sql("""
+ select
+ item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom {cf_field}
+ from
+ `tabItem` item
{cf_join}
- WHERE item.`name` IN ({names}) AND IFNULL(item.`disabled`, 0) = 0
- """.format(cf_field=cf_field, cf_join=cf_join, names=', '.join([frappe.db.escape(i, percent=False) for i in items])),
- {"include_uom": filters.get("include_uom")}, as_dict=1):
- item_details.setdefault(item.name, item)
+ where
+ item.name in ({item_codes}) and ifnull(item.disabled, 0) = 0
+ """.format(cf_field=cf_field, cf_join=cf_join, item_codes=item_codes), as_dict=1)
+
+ for item in res:
+ item_details.setdefault(item.name, item)
if filters.get('show_variant_attributes', 0) == 1:
variant_values = get_variant_values_for(list(item_details))