Merge branch 'master' into develop
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 1a98835..42f538d 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -4,7 +4,7 @@
import frappe
from erpnext.hooks import regional_overrides
-__version__ = '9.1.5'
+__version__ = '9.1.6'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py
index ccf8a84..04f7e1b 100644
--- a/erpnext/accounts/doctype/sales_invoice/pos.py
+++ b/erpnext/accounts/doctype/sales_invoice/pos.py
@@ -486,17 +486,21 @@
if frappe.message_log: frappe.message_log.pop()
frappe.db.rollback()
frappe.log_error(frappe.get_traceback())
- name_list = save_invoice(e, si_doc, name, name_list)
+ name_list = save_invoice(doc, name, name_list)
return name_list
-def save_invoice(e, si_doc, name, name_list):
+def save_invoice(doc, name, name_list):
try:
if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}):
- si_doc.docstatus = 0
- si_doc.flags.ignore_mandatory = True
- si_doc.due_date = si_doc.posting_date
- si_doc.insert()
+ si = frappe.new_doc('Sales Invoice')
+ si.update(doc)
+ si.set_posting_time = 1
+ si.customer = get_customer_id(doc)
+ si.due_date = doc.get('posting_date')
+ si.flags.ignore_mandatory = True
+ si.insert(ignore_permissions=True)
+ frappe.db.commit()
name_list.append(name)
except Exception:
frappe.log_error(frappe.get_traceback())
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 900a6e9..264f027 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -3,8 +3,8 @@
from __future__ import unicode_literals
import frappe
-import unittest, copy
-from frappe.utils import nowdate, add_days, flt
+import unittest, copy, time
+from frappe.utils import nowdate, add_days, flt, cint
from frappe.model.dynamic_links import get_dynamic_link_map
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
@@ -665,6 +665,47 @@
self.pos_gl_entry(si, pos, 330)
+ def test_make_pos_invoice_in_draft(self):
+ from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
+ from erpnext.stock.doctype.item.test_item import make_item
+
+ set_perpetual_inventory()
+
+ allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
+ if allow_negative_stock:
+ frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0)
+
+ make_pos_profile()
+ timestamp = cint(time.time())
+
+ item = make_item("_Test POS Item")
+ pos = copy.deepcopy(test_records[1])
+ pos['items'][0]['item_code'] = item.name
+ pos["is_pos"] = 1
+ pos["offline_pos_name"] = timestamp
+ pos["update_stock"] = 1
+ pos["payments"] = [{'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 300},
+ {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 330}]
+
+ invoice_data = [{timestamp: pos}]
+ si = make_invoice(invoice_data).get('invoice')
+ self.assertEquals(si[0], timestamp)
+
+ sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp})
+ self.assertEquals(sales_invoice[0].docstatus, 0)
+
+ timestamp = cint(time.time())
+ pos["offline_pos_name"] = timestamp
+ invoice_data = [{timestamp: pos}]
+ si1 = make_invoice(invoice_data).get('invoice')
+ self.assertEquals(si1[0], timestamp)
+
+ sales_invoice1 = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp})
+ self.assertEquals(sales_invoice1[0].docstatus, 0)
+
+ if allow_negative_stock:
+ frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 1)
+
def pos_gl_entry(self, si, pos, cash_amount):
# check stock ledger entries
sle = frappe.db.sql("""select * from `tabStock Ledger Entry`
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 07f6979..78e3faa 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -107,6 +107,8 @@
def process(self):
self.grouped = {}
+ self.grouped_data = []
+
for row in self.si_list:
if self.skip_row(row, self.product_bundles):
continue
@@ -150,7 +152,6 @@
def get_average_rate_based_on_group_by(self):
# sum buying / selling totals for group
- self.grouped_data = []
for key in self.grouped.keys():
if self.filters.get("group_by") != "Invoice":
for i, row in enumerate(self.grouped[key]):
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
index 44068ce..50d6abd 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
@@ -816,7 +816,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-07-21 14:06:46.309322",
+ "modified": "2017-10-17 17:27:06.281494",
"modified_by": "Administrator",
"module": "Buying",
"name": "Request for Quotation",
@@ -909,26 +909,6 @@
"cancel": 0,
"create": 0,
"delete": 0,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Supplier",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
- },
- {
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py
index 513b97f..8ca4f70 100644
--- a/erpnext/controllers/item_variant.py
+++ b/erpnext/controllers/item_variant.py
@@ -205,9 +205,10 @@
if item.variant_based_on=='Item Attribute':
if variant.attributes:
- variant.description += "\n"
- for d in variant.attributes:
- variant.description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
+ if not variant.description:
+ variant.description += "\n"
+ for d in variant.attributes:
+ variant.description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
def make_variant_item_code(template_item_code, template_item_name, variant):
"""Uses template's item code and abbreviations to make variant's item code"""
diff --git a/erpnext/healthcare/doctype/consultation/consultation.py b/erpnext/healthcare/doctype/consultation/consultation.py
index b8155b9..e16c221 100755
--- a/erpnext/healthcare/doctype/consultation/consultation.py
+++ b/erpnext/healthcare/doctype/consultation/consultation.py
@@ -78,7 +78,7 @@
create_invoice_items(physician, sales_invoice, company)
sales_invoice.save(ignore_permissions=True)
- frappe.db.sql(_("""update tabConsultation set invoice='{0}' where name='{1}'""").format(sales_invoice.name, consultation_id))
+ frappe.db.sql("""update tabConsultation set invoice=%s where name=%s""", (sales_invoice.name, consultation_id))
appointment = frappe.db.get_value("Consultation", consultation_id, "appointment")
if appointment:
frappe.db.set_value("Patient Appointment", appointment, "sales_invoice", sales_invoice.name)
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py
index 0daf9cb..6fd9535 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test.py
+++ b/erpnext/healthcare/doctype/lab_test/lab_test.py
@@ -291,5 +291,5 @@
@frappe.whitelist()
def get_lab_test_prescribed(patient):
- return frappe.db.sql(_("""select cp.name, cp.test_code, cp.parent, cp.invoice, ct.physician, ct.consultation_date from tabConsultation ct,
- `tabLab Prescription` cp where ct.patient='{0}' and cp.parent=ct.name and cp.test_created=0""").format(patient))
+ return frappe.db.sql("""select cp.name, cp.test_code, cp.parent, cp.invoice, ct.physician, ct.consultation_date from tabConsultation ct,
+ `tabLab Prescription` cp where ct.patient=%s and cp.parent=ct.name and cp.test_created=0""", (patient))
diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py
index 98526cc..f4d9a43 100644
--- a/erpnext/healthcare/doctype/patient/patient.py
+++ b/erpnext/healthcare/doctype/patient/patient.py
@@ -111,10 +111,10 @@
@frappe.whitelist()
def get_patient_detail(patient, company=None):
- patient_dict = frappe.db.sql(_("""select * from tabPatient where name='{0}'""").format(patient), as_dict=1)
+ patient_dict = frappe.db.sql("""select * from tabPatient where name=%s""", (patient), as_dict=1)
if not patient_dict:
frappe.throw("Patient not found")
- vital_sign = frappe.db.sql(_("""select * from `tabVital Signs` where patient='{0}' order by signs_date desc limit 1""").format(patient), as_dict=1)
+ vital_sign = frappe.db.sql("""select * from `tabVital Signs` where patient=%s order by signs_date desc limit 1""", (patient), as_dict=1)
details = patient_dict[0]
if vital_sign:
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
index eab2f2d..2647034 100755
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
@@ -125,7 +125,7 @@
create_invoice_items(appointment_id, physician, company, sales_invoice)
sales_invoice.save(ignore_permissions=True)
- frappe.db.sql(_("""update `tabPatient Appointment` set sales_invoice='{0}' where name='{1}'""").format(sales_invoice.name, appointment_id))
+ frappe.db.sql("""update `tabPatient Appointment` set sales_invoice=%s where name=%s""", (sales_invoice.name, appointment_id))
frappe.db.set_value("Fee Validity", fee_validity.name, "ref_invoice", sales_invoice.name)
consultation = frappe.db.exists({
"doctype": "Consultation",
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index ead5d33..b140bf5 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -147,22 +147,28 @@
if arg.get('scrap_items'):
rate = self.get_valuation_rate(arg)
elif arg:
- if self.rm_cost_as_per == 'Valuation Rate':
- rate = self.get_valuation_rate(arg)
- elif self.rm_cost_as_per == 'Last Purchase Rate':
- rate = arg['last_purchase_rate'] \
- or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")
- elif self.rm_cost_as_per == "Price List":
- if not self.buying_price_list:
- frappe.throw(_("Please select Price List"))
- rate = frappe.db.get_value("Item Price",
- {"price_list": self.buying_price_list, "item_code": arg["item_code"]}, "price_list_rate")
- price_list_currency = frappe.db.get_value("Price List", self.buying_price_list, "currency")
- if price_list_currency != self.company_currency():
- rate = flt(rate * self.conversion_rate)
-
- if arg['bom_no'] and (not rate or self.set_rate_of_sub_assembly_item_based_on_bom):
+ if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom:
rate = self.get_bom_unitcost(arg['bom_no'])
+ else:
+ if self.rm_cost_as_per == 'Valuation Rate':
+ rate = self.get_valuation_rate(arg)
+ elif self.rm_cost_as_per == 'Last Purchase Rate':
+ rate = arg.get('last_purchase_rate') \
+ or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")
+ elif self.rm_cost_as_per == "Price List":
+ if not self.buying_price_list:
+ frappe.throw(_("Please select Price List"))
+ rate = frappe.db.get_value("Item Price", {"price_list": self.buying_price_list,
+ "item_code": arg["item_code"]}, "price_list_rate")
+
+ price_list_currency = frappe.db.get_value("Price List",
+ self.buying_price_list, "currency")
+ if price_list_currency != self.company_currency():
+ rate = flt(rate * self.conversion_rate)
+
+ if not rate:
+ frappe.msgprint(_("{0} not found for Item {1}")
+ .format(self.rm_cost_as_per, arg["item_code"]))
return flt(rate)
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 9492c11..ef34189 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -452,3 +452,4 @@
erpnext.patches.v9_0.set_schedule_date_for_material_request_and_purchase_order
erpnext.patches.v9_0.student_admission_childtable_migrate
erpnext.patches.v9_0.add_healthcare_domain
+erpnext.patches.v9_0.set_variant_item_description
diff --git a/erpnext/patches/v9_0/set_variant_item_description.py b/erpnext/patches/v9_0/set_variant_item_description.py
new file mode 100644
index 0000000..c844571
--- /dev/null
+++ b/erpnext/patches/v9_0/set_variant_item_description.py
@@ -0,0 +1,45 @@
+import frappe
+from frappe.utils import cstr
+
+def execute():
+ '''
+ Issue:
+ While copying data from template item to variant item,
+ the system appending description multiple times to the respective variant.
+
+ Purpose:
+ Check variant description,
+ if variant have user defined description remove all system appended descriptions
+ else replace multiple system generated descriptions with single description
+
+ Steps:
+ 1. Get all variant items
+ 2. Create system generated variant description
+ 3. If variant have user defined description, remove all system generated descriptions
+ 4. If variant description only contains system generated description,
+ replace multiple descriptions by new description.
+ '''
+ for item in frappe.db.sql(""" select name from tabItem
+ where ifnull(variant_of, '') != '' """,as_dict=1):
+ variant = frappe.get_doc("Item", item.name)
+ temp_variant_description = '\n'
+
+ if variant.attributes:
+ for d in variant.attributes:
+ temp_variant_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
+
+ variant_description = variant.description.replace(temp_variant_description, '').rstrip()
+ if variant_description:
+ splitted_desc = variant.description.strip().split(temp_variant_description)
+
+ if len(splitted_desc) > 2:
+ if splitted_desc[0] == '':
+ variant_description = temp_variant_description + variant_description
+ elif splitted_desc[1] == '' or splitted_desc[1] == '\n':
+ variant_description += temp_variant_description
+ variant.db_set('description', variant_description, update_modified=False)
+ else:
+ variant.db_set('description', variant_description, update_modified=False)
+
+ else:
+ variant.db_set('description', temp_variant_description, update_modified=False)
\ No newline at end of file
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index ba1414c..1ea5962 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -159,9 +159,14 @@
});
var calculate_end_time = function(frm, cdt, cdn) {
- var child = locals[cdt][cdn];
+ let child = locals[cdt][cdn];
- var d = moment(child.from_time);
+ if(!child.from_time) {
+ // if from_time value is not available then set the current datetime
+ frappe.model.set_value(cdt, cdn, "from_time", frappe.datetime.get_datetime_as_string());
+ }
+
+ let d = moment(child.from_time);
if(child.hours) {
d.add(child.hours, "hours");
frm._setting_hours = true;