diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 3f6504d7956..c06ccca3aa5 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe -__version__ = '8.0.13' +__version__ = '8.0.14' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 7a4d40d6ee4..4f268a24ccf 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "beta": 0, @@ -25,7 +26,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Make Accounting Entry For Every Stock Movement", "length": 0, "no_copy": 0, @@ -33,6 +36,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -51,7 +55,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Accounts Frozen Upto", "length": 0, "no_copy": 0, @@ -59,6 +65,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -77,7 +84,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries", "length": 0, "no_copy": 0, @@ -86,6 +95,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -103,7 +113,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, + "in_standard_filter": 0, "length": 0, "no_copy": 0, "permlevel": 0, @@ -111,6 +123,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -129,7 +142,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Credit Controller", "length": 0, "no_copy": 0, @@ -138,6 +153,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -155,7 +171,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Check Supplier Invoice Number Uniqueness", "length": 0, "no_copy": 0, @@ -164,6 +182,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -181,7 +200,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Make Payment via Journal Entry", "length": 0, "no_copy": 0, @@ -190,6 +211,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -208,7 +230,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Unlink Payment on Cancellation of Invoice", "length": 0, "no_copy": 0, @@ -217,6 +241,37 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "fieldname": "book_asset_depreciation_entry_automatically", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Book Asset Depreciation Entry Automatically", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -224,18 +279,18 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, - "icon": "fa fa-cog", + "icon": "icon-cog", "idx": 1, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2016-10-20 16:12:38.595075", + "modified": "2017-04-18 13:35:59.166250", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", @@ -251,7 +306,6 @@ "export": 0, "if_owner": 0, "import": 0, - "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, @@ -266,6 +320,8 @@ "quick_entry": 1, "read_only": 0, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_order": "ASC", + "track_changes": 1, "track_seen": 0 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset/depreciation.py b/erpnext/accounts/doctype/asset/depreciation.py index 15c155c7ac5..397342dc46f 100644 --- a/erpnext/accounts/doctype/asset/depreciation.py +++ b/erpnext/accounts/doctype/asset/depreciation.py @@ -8,6 +8,10 @@ from frappe import _ from frappe.utils import flt, today, getdate def post_depreciation_entries(date=None): + # Return if automatic booking of asset depreciation is disabled + if not frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically"): + return + if not date: date = today() for asset in get_depreciable_assets(date): diff --git a/erpnext/accounts/doctype/asset/test_asset.py b/erpnext/accounts/doctype/asset/test_asset.py index 51496b918c9..000bc5ccd25 100644 --- a/erpnext/accounts/doctype/asset/test_asset.py +++ b/erpnext/accounts/doctype/asset/test_asset.py @@ -166,6 +166,23 @@ class TestAsset(unittest.TestCase): self.assertEqual(gle, expected_gle) self.assertEqual(asset.get("value_after_depreciation"), 70000) + + def test_depreciation_entry_cancellation(self): + asset = frappe.get_doc("Asset", "Macbook Pro 1") + asset.submit() + post_depreciation_entries(date="2021-01-01") + + asset.load_from_db() + + # cancel depreciation entry + depr_entry = asset.get("schedules")[0].journal_entry + self.assertTrue(depr_entry) + frappe.get_doc("Journal Entry", depr_entry).cancel() + + asset.load_from_db() + depr_entry = asset.get("schedules")[0].journal_entry + self.assertFalse(depr_entry) + def test_scrap_asset(self): asset = frappe.get_doc("Asset", "Macbook Pro 1") @@ -297,4 +314,7 @@ def set_depreciation_settings_in_company(): company.depreciation_expense_account = "_Test Depreciations - _TC" company.disposal_account = "_Test Gain/Loss on Asset Disposal - _TC" company.depreciation_cost_center = "_Test Cost Center - _TC" - company.save() \ No newline at end of file + company.save() + + # Enable booking asset depreciation entry automatically + frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1) \ No newline at end of file diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index a471c48c575..b9b8fc29685 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -72,6 +72,7 @@ class JournalEntry(AccountsController): self.update_expense_claim() self.update_employee_loan() self.unlink_advance_entry_reference() + self.unlink_asset_reference() def unlink_advance_entry_reference(self): for d in self.get("accounts"): @@ -81,6 +82,18 @@ class JournalEntry(AccountsController): d.reference_type = '' d.reference_name = '' d.db_update() + + def unlink_asset_reference(self): + for d in self.get("accounts"): + if d.reference_type=="Asset" and d.reference_name: + asset = frappe.get_doc("Asset", d.reference_name) + for s in asset.get("schedules"): + if s.journal_entry == self.name: + s.db_set("journal_entry", None) + asset.value_after_depreciation += s.depreciation_amount + + asset.db_set("value_after_depreciation", asset.value_after_depreciation) + asset.set_status() def validate_party(self): for d in self.get("accounts"): diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 93685dbbaf9..5af0d9f1be6 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -23,6 +23,8 @@ class PricingRule(Document): self.validate_price_or_discount() self.validate_max_discount() + if not self.margin_type: self.margin_rate_or_amount = 0.0 + def validate_mandatory(self): for field in ["apply_on", "applicable_for"]: tocheck = frappe.scrub(self.get(field) or "") @@ -143,7 +145,7 @@ def get_pricing_rule_for_item(args): }) if args.ignore_pricing_rule or not args.item_code: - if frappe.db.exists(args.doctype, args.name) and args.get("pricing_rule"): + if args.get("pricing_rule"): item_details = remove_pricing_rule(args, item_details) return item_details @@ -178,7 +180,7 @@ def get_pricing_rule_for_item(args): item_details.margin_rate_or_amount = pricing_rule.margin_rate_or_amount if pricing_rule.price_or_discount == "Price": item_details.update({ - "price_list_rate": pricing_rule.price/flt(args.conversion_rate) \ + "price_list_rate": (pricing_rule.price/flt(args.conversion_rate)) * args.conversion_factor or 1.0 \ if args.conversion_rate else 0.0, "discount_percentage": 0.0 }) diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index bf370c86845..36e5a1d8b27 100755 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -1422,7 +1422,7 @@ "unique": 0 }, { - "allow_on_submit": 1, + "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, @@ -1508,7 +1508,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -1939,7 +1939,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-04-17 13:44:17.460674", + "modified": "2017-04-19 11:54:16.112134", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", @@ -1953,4 +1953,4 @@ "sort_order": "DESC", "track_changes": 0, "track_seen": 0 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 419c5579e0b..f1f997a7f69 100644 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -32,7 +32,7 @@ def get_pos_data(): 'doc': doc, 'default_customer': pos_profile.get('customer'), 'items': get_items_list(pos_profile), - 'item_groups': get_item_group(pos_profile), + 'item_groups': get_item_groups(pos_profile), 'customers': customers, 'address': get_customers_address(customers), 'serial_no_data': get_serial_no_data(pos_profile, doc.company), @@ -132,7 +132,7 @@ def get_items_list(pos_profile): if pos_profile.get('item_groups'): # Get items based on the item groups defined in the POS profile for d in pos_profile.get('item_groups'): - item_groups.extend(get_child_nodes('Item Group', d.item_group)) + item_groups.extend([d.name for d in get_child_nodes('Item Group', d.item_group)]) cond = "item_group in (%s)"%(', '.join(['%s']*len(item_groups))) return frappe.db.sql(""" @@ -146,14 +146,19 @@ def get_items_list(pos_profile): disabled = 0 and has_variants = 0 and is_sales_item = 1 and {cond} """.format(cond=cond), tuple(item_groups), as_dict=1) -def get_item_group(pos_profile): +def get_item_groups(pos_profile): + item_group_dict = {} if pos_profile.get('item_groups'): item_groups = [] for d in pos_profile.get('item_groups'): item_groups.extend(get_child_nodes('Item Group', d.item_group)) - return item_groups else: - return frappe.db.sql_list("""Select name from `tabItem Group` order by name""") + item_groups = frappe.db.sql("""Select name, + lft, rgt from `tabItem Group` order by lft""", as_dict=1) + + for data in item_groups: + item_group_dict[data.name] = [data.lft, data.rgt] + return item_group_dict def get_customers_list(pos_profile): cond = "1=1" @@ -161,7 +166,7 @@ def get_customers_list(pos_profile): if pos_profile.get('customer_groups'): # Get customers based on the customer groups defined in the POS profile for d in pos_profile.get('customer_groups'): - customer_groups.extend(get_child_nodes('Customer Group', d.customer_group)) + customer_groups.extend([d.name for d in get_child_nodes('Customer Group', d.customer_group)]) cond = "customer_group in (%s)"%(', '.join(['%s']*len(customer_groups))) return frappe.db.sql(""" select name, customer_name, customer_group, @@ -187,8 +192,8 @@ def get_customers_address(customers): def get_child_nodes(group_type, root): lft, rgt = frappe.db.get_value(group_type, root, ["lft", "rgt"]) - return frappe.db.sql_list(""" Select name from `tab{tab}` where - lft >= {lft} and rgt <= {rgt}""".format(tab=group_type, lft=lft, rgt=rgt)) + return frappe.db.sql(""" Select name, lft, rgt from `tab{tab}` where + lft >= {lft} and rgt <= {rgt} order by lft""".format(tab=group_type, lft=lft, rgt=rgt), as_dict=1) def get_serial_no_data(pos_profile, company): # get itemwise serial no data diff --git a/erpnext/accounts/doctype/sales_invoice/test_records.json b/erpnext/accounts/doctype/sales_invoice/test_records.json index 732c4465a80..0b7b76d1b3b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_records.json +++ b/erpnext/accounts/doctype/sales_invoice/test_records.json @@ -147,9 +147,9 @@ "price_list_rate": 50, "qty": 10, "rate": 50, - "uom": "_Test UOM", + "uom": "_Test UOM 1", "conversion_factor": 1, - "stock_uom": "_Test UOM" + "stock_uom": "_Test UOM 1" }, { "cost_center": "_Test Cost Center - _TC", @@ -273,9 +273,9 @@ "parentfield": "items", "price_list_rate": 62.5, "qty": 10, - "uom": "_Test UOM", - "conversion_factor": 1, - "stock_uom": "_Test UOM" + "uom": "_Test UOM 1", + "conversion_factor": 1, + "stock_uom": "_Test UOM 1" }, { diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 305b689e15b..0ada8478a10 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1043,6 +1043,25 @@ class TestSalesInvoice(unittest.TestCase): #check outstanding after advance cancellation self.assertEqual(flt(si.outstanding_amount), flt(si.grand_total + si.total_advance, si.precision("outstanding_amount"))) + def test_multiple_uom_in_selling(self): + si = frappe.copy_doc(test_records[1]) + + si.items[0].uom = "_Test UOM 1" + si.items[0].conversion_factor = None + si.items[0].price_list_rate = None + si.save() + + expected_values = { + "keys": ["price_list_rate", "stock_uom", "uom", "conversion_factor", "rate", "amount", + "base_price_list_rate", "base_rate", "base_amount"], + "_Test Item": [1000, "_Test UOM", "_Test UOM 1", 10.0, 1000, 1000, 1000, 1000, 1000] + } + + # check if the conversion_factor and price_list_rate is calculated according to uom + for d in si.get("items"): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.get(k), expected_values[d.item_code][i]) + def create_sales_invoice(**args): si = frappe.new_doc("Sales Invoice") args = frappe._dict(args) diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 205b61e8821..80f8b57fb99 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -1548,7 +1548,7 @@ "unique": 0 }, { - "allow_on_submit": 1, + "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, @@ -2094,7 +2094,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-04-05 23:28:13.520429", + "modified": "2017-04-19 11:53:26.682964", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 670a577c4e7..70067614b6b 100644 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -407,8 +407,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }); this.search_item_group = this.wrapper.find('.search-item-group'); - - var dropdown_html = me.item_groups.map(function(item_group) { + sorted_item_groups = this.get_sorted_item_groups() + var dropdown_html = sorted_item_groups.map(function(item_group) { return "
  • "+item_group+"
  • "; }).join(""); @@ -437,6 +437,15 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }) }, + get_sorted_item_groups: function() { + list = {} + $.each(this.item_groups, function(i, data) { + list[i] = data[0] + }) + + return Object.keys(list).sort(function(a,b){return list[a]-list[b]}) + }, + toggle_more_btn: function() { if(!this.items || this.items.length <= this.page_len) { this.wrapper.find(".btn-more").hide(); @@ -1091,9 +1100,9 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ // $(me.wrapper).find(".pos-item-wrapper").on("click", function () { $(this.wrapper).on("click", ".pos-item-wrapper", function () { me.item_code = ''; + me.customer_validate(); if($(me.pos_bill).is(":hidden")) return; - me.customer_validate(); if (me.frm.doc.docstatus == 0) { me.items = me.get_items($(this).attr("data-item-code")) me.add_to_cart(); @@ -1768,7 +1777,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ var me = this; return $.grep(this.pricing_rules, function (data) { if (item.qty >= data.min_qty && (item.qty <= (data.max_qty ? data.max_qty : item.qty))) { - if (data.item_code == item.item_code || in_list(['All Item Groups', item.item_group], data.item_group) || item.brand == data.brand) { + if (me.validate_item_condition(data, item)) { if (in_list(['Customer', 'Customer Group', 'Territory', 'Campaign'], data.applicable_for)) { return me.validate_condition(data) } else { @@ -1779,6 +1788,26 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }) }, + validate_item_condition: function (data, item) { + var apply_on = frappe.model.scrub(data.apply_on); + + return (data.apply_on == 'Item Group') + ? this.validate_item_group(data.item_group, item.item_group) : (data[apply_on] == item[apply_on]); + }, + + validate_item_group: function (pr_item_group, cart_item_group) { + //pr_item_group = pricing rule's item group + //cart_item_group = cart item's item group + //this.item_groups has information about item group's lft and rgt + //for example: {'Foods': [12, 19]} + + pr_item_group = this.item_groups[pr_item_group] + cart_item_group = this.item_groups[cart_item_group] + + return (cart_item_group[0] >= pr_item_group[0] && + cart_item_group[1] <= pr_item_group[1]) + }, + validate_condition: function (data) { //This method check condition based on applicable for condition = this.get_mapper_for_pricing_rule(data)[data.applicable_for] diff --git a/erpnext/accounts/print_format/point_of_sale/point_of_sale.json b/erpnext/accounts/print_format/point_of_sale/point_of_sale.json index 6b603c80e23..b413321bfd4 100644 --- a/erpnext/accounts/print_format/point_of_sale/point_of_sale.json +++ b/erpnext/accounts/print_format/point_of_sale/point_of_sale.json @@ -7,10 +7,10 @@ "docstatus": 0, "doctype": "Print Format", "font": "Default", - "html": "\n\n

    \n\t{{ company }}
    \n\t{{ __(\"POS No : \") }}{{offline_pos_name}}
    \n

    \n

    \n\t{{ __(\"Date\") }}: {{ dateutil.global_date_format(posting_date) }}
    \n

    \n\n
    \n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{% for item in items %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endfor %}\n\t\n
    {{ __(\"Item\") }}{{ __(\"Qty\") }}{{ __(\"Amount\") }}
    \n\t\t\t\t{{ item.item_name }}\n\t\t\t{{ format_number(item.qty, null,precision(\"difference\")) }}
    @ {{ format_currency(item.rate, currency) }}
    {{ format_currency(item.amount, currency) }}
    \n\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% for row in taxes %}\n\t\t{% if not row.included_in_print_rate %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t{% endfor %}\n\t\t{% if discount_amount %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n
    \n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t
    \n\t\t\t\t{{ row.description }}\n\t\t\t\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t
    \n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t
    \n\t\t\t\t{{ __(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t
    \n\t\t\t\t{{ __(\"Paid Amount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t
    \n\n\n
    \n

    {{ terms }}

    \n

    {{ __(\"Thank you, please visit again.\") }}

    ", + "html": "\n\n

    \n\t{{ company }}
    \n\t{{ __(\"POS No : \") }} {{ offline_pos_name }}
    \n

    \n

    \n\t{{ __(\"Date\") }}: {{ dateutil.global_date_format(posting_date) }}
    \n

    \n\n
    \n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{% for item in items %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endfor %}\n\t\n
    {{ __(\"Item\") }}{{ __(\"Qty\") }}{{ __(\"Amount\") }}
    \n\t\t\t\t{{ item.item_name }}\n\t\t\t{{ format_number(item.qty, null,precision(\"difference\")) }}
    @ {{ format_currency(item.rate, currency) }}
    {{ format_currency(item.amount, currency) }}
    \n\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% for row in taxes %}\n\t\t{% if not row.included_in_print_rate %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t{% endfor %}\n\t\t{% if discount_amount %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n
    \n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t
    \n\t\t\t\t{{ row.description }}\n\t\t\t\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t
    \n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t
    \n\t\t\t\t{{ __(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t
    \n\t\t\t\t{{ __(\"Paid Amount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t
    \n\n\n
    \n

    {{ terms }}

    \n

    {{ __(\"Thank you, please visit again.\") }}

    ", "idx": 0, "line_breaks": 0, - "modified": "2017-04-17 12:12:00.153763", + "modified": "2017-04-19 13:28:05.129504", "modified_by": "Administrator", "module": "Accounts", "name": "Point of Sale", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index e776b3ea981..ecce1184d6d 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 0, "autoname": "naming_series:", @@ -341,7 +342,7 @@ "remember_last_selected_value": 1, "report_hide": 0, "reqd": 1, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -1207,6 +1208,8 @@ "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Link to material requests", @@ -3185,18 +3188,18 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-file-text", "idx": 105, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 1, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-02-28 18:20:15.650815", + "modified": "2017-04-18 18:49:49.535066", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", @@ -3294,4 +3297,4 @@ "title_field": "title", "track_changes": 0, "track_seen": 0 -} +} \ No newline at end of file diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 12d0da85d77..0037745817b 100755 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "autoname": "hash", @@ -98,7 +99,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -156,7 +157,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -799,6 +800,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "permlevel": 0, + "precision": "9", "print_hide": 1, "print_hide_if_no_value": 0, "print_width": "100px", @@ -1647,17 +1649,17 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 1, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-02-17 16:44:55.434162", + "modified": "2017-04-18 18:49:08.604055", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 910c19c9ca0..742e59b4d69 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -186,15 +186,19 @@ class AccountsController(TransactionBase): ret = get_item_details(args) - for fieldname, value in ret.items(): if item.meta.get_field(fieldname) and value is not None: if (item.get(fieldname) is None or fieldname in force_item_fields): item.set(fieldname, value) - elif fieldname == "cost_center" and not item.get("cost_center"): + elif fieldname in ['cost_center', 'conversion_factor'] and not item.get(fieldname): item.set(fieldname, value) + elif fieldname == "serial_no": + stock_qty = item.get("stock_qty") * -1 if item.get("stock_qty") < 0 else item.get("stock_qty") + if stock_qty != len(item.get('serial_no').split('\n')): + item.set(fieldname, value) + elif fieldname == "conversion_factor" and not item.get("conversion_factor"): item.set(fieldname, value) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index af51f70147f..1cd705b8c47 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -41,7 +41,7 @@ class SellingController(StockController): # set contact and address details for customer, if they are not mentioned self.set_missing_lead_customer_details() - self.set_price_list_and_item_details() + self.set_price_list_and_item_details(for_validate=for_validate) def set_missing_lead_customer_details(self): if getattr(self, "customer", None): @@ -60,9 +60,9 @@ class SellingController(StockController): posting_date=self.get('transaction_date') or self.get('posting_date'), company=self.company)) - def set_price_list_and_item_details(self): + def set_price_list_and_item_details(self, for_validate=False): self.set_price_list_currency("Selling") - self.set_missing_item_details() + self.set_missing_item_details(for_validate=for_validate) def apply_shipping_rule(self): if self.shipping_rule: diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 0355c269d0b..362d07515bc 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -60,6 +60,7 @@ class calculate_taxes_and_totals(object): if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item']: item.total_margin = self.calculate_margin(item) + item.rate = flt(item.total_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))\ if item.total_margin > 0 else item.rate diff --git a/erpnext/demo/user/fixed_asset.py b/erpnext/demo/user/fixed_asset.py index bf3199ea5f9..b2db39c9f07 100644 --- a/erpnext/demo/user/fixed_asset.py +++ b/erpnext/demo/user/fixed_asset.py @@ -18,6 +18,9 @@ def work(): # fixed_asset.work() already run return + # Enable booking asset depreciation entry automatically + frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1) + # post depreciation entries as on today post_depreciation_entries() diff --git a/erpnext/docs/user/manual/en/stock/item/purchase-details.md b/erpnext/docs/user/manual/en/stock/item/purchase-details.md index 5749178448b..47983dfc625 100644 --- a/erpnext/docs/user/manual/en/stock/item/purchase-details.md +++ b/erpnext/docs/user/manual/en/stock/item/purchase-details.md @@ -1,4 +1,4 @@ -# purchase details +# Purchase Details # How Do I Track Warranty Status? diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index 72fa47dd815..458b2dd5c19 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -35,9 +35,11 @@ class Attendance(Document): frappe.throw(_("No leave record found for employee {0} for {1}").format(self.employee, self.attendance_date)) def validate_attendance_date(self): + date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining") + if getdate(self.attendance_date) > getdate(nowdate()): frappe.throw(_("Attendance can not be marked for future dates")) - elif getdate(self.attendance_date) < frappe.db.get_value("Employee", self.employee, "date_of_joining"): + elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining): frappe.throw(_("Attendance date can not be less than employee's joining date")) def validate_employee(self): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 9c2b1c49ad1..111cd65dc22 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -385,4 +385,7 @@ erpnext.patches.v8_0.addresses_linked_to_lead execute:frappe.delete_doc('DocType', 'Purchase Common') erpnext.patches.v8_0.update_stock_qty_value_in_purchase_invoice erpnext.patches.v8_0.update_supplier_address_in_stock_entry -erpnext.patches.v8_0.rename_is_sample_item_to_allow_zero_valuation_rate \ No newline at end of file +erpnext.patches.v8_0.rename_is_sample_item_to_allow_zero_valuation_rate +erpnext.patches.v8_0.set_null_to_serial_nos_for_disabled_sales_invoices +erpnext.patches.v8_0.enable_booking_asset_depreciation_automatically +erpnext.patches.v8_0.set_project_copied_from diff --git a/erpnext/patches/v8_0/enable_booking_asset_depreciation_automatically.py b/erpnext/patches/v8_0/enable_booking_asset_depreciation_automatically.py new file mode 100644 index 00000000000..1088d702dd0 --- /dev/null +++ b/erpnext/patches/v8_0/enable_booking_asset_depreciation_automatically.py @@ -0,0 +1,9 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.db.set_value("Accounts Settings", None, + "book_asset_depreciation_entry_automatically", 1) \ No newline at end of file diff --git a/erpnext/patches/v8_0/rename_is_sample_item_to_allow_zero_valuation_rate.py b/erpnext/patches/v8_0/rename_is_sample_item_to_allow_zero_valuation_rate.py index 744cfed9983..e517df5fdb9 100644 --- a/erpnext/patches/v8_0/rename_is_sample_item_to_allow_zero_valuation_rate.py +++ b/erpnext/patches/v8_0/rename_is_sample_item_to_allow_zero_valuation_rate.py @@ -9,4 +9,5 @@ def execute(): for doctype in doc_list: frappe.reload_doctype(doctype) - rename_field(doctype, "is_sample_item", "allow_zero_valuation_rate") \ No newline at end of file + if "is_sample_item" in frappe.db.get_table_columns(doctype): + rename_field(doctype, "is_sample_item", "allow_zero_valuation_rate") \ No newline at end of file diff --git a/erpnext/patches/v8_0/set_null_to_serial_nos_for_disabled_sales_invoices.py b/erpnext/patches/v8_0/set_null_to_serial_nos_for_disabled_sales_invoices.py new file mode 100644 index 00000000000..197d6ded61a --- /dev/null +++ b/erpnext/patches/v8_0/set_null_to_serial_nos_for_disabled_sales_invoices.py @@ -0,0 +1,14 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty + +def execute(): + frappe.db.sql(""" + update + `tabSales Invoice Item` + set serial_no = NULL + where + parent in (select name from `tabSales Invoice` where update_stock = 0 and docstatus = 1)""") \ No newline at end of file diff --git a/erpnext/patches/v8_0/set_project_copied_from.py b/erpnext/patches/v8_0/set_project_copied_from.py new file mode 100644 index 00000000000..d4287978cf8 --- /dev/null +++ b/erpnext/patches/v8_0/set_project_copied_from.py @@ -0,0 +1,11 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doctype("Project") + + frappe.db.sql(''' + UPDATE `tabProject` + SET copied_from=name + WHERE copied_from is NULL + ''') \ No newline at end of file diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index e809328df97..32a3ffd3351 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, "autoname": "field:project_name", @@ -553,6 +554,35 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "copied_from", + "fieldtype": "Data", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Copied From", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -1052,7 +1082,7 @@ "search_index": 0, "set_only_once": 0, "unique": 0 - }, + }, { "allow_on_submit": 0, "bold": 0, @@ -1174,19 +1204,19 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-puzzle-piece", "idx": 29, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 4, - "modified": "2017-02-17 17:24:04.146872", - "modified_by": "Administrator", + "modified": "2017-04-19 13:16:32.462005", + "modified_by": "faris@erpnext.com", "module": "Projects", "name": "Project", "owner": "Administrator", @@ -1261,4 +1291,4 @@ "timeline_field": "customer", "track_changes": 0, "track_seen": 1 -} +} \ No newline at end of file diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 37734f18760..40493e1ed61 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -205,6 +205,32 @@ class Project(Document): def on_update(self): self.load_tasks() self.sync_tasks() + self.update_dependencies_on_duplicated_project() + + def update_dependencies_on_duplicated_project(self): + if self.flags.dont_sync_tasks: return + if not self.copied_from: + self.copied_from = self.name + + if self.name != self.copied_from and self.get('__unsaved'): + # duplicated project + dependency_map = {} + for task in self.tasks: + name, depends_on_tasks = frappe.db.get_value( + 'Task', { "subject": task.title, "project": self.copied_from }, ['name', 'depends_on_tasks'] + ) + depends_on_tasks = [x for x in depends_on_tasks.split(',') if x] + dependency_map[task.title] = [ x['subject'] for x in frappe.get_list( + 'Task Depends On', {"parent": name}, ['subject'])] + + for key, value in dependency_map.iteritems(): + task_name = frappe.db.get_value('Task', {"subject": key, "project": self.name }) + task_doc = frappe.get_doc('Task', task_name) + + for dt in value: + dt_name = frappe.db.get_value('Task', {"subject": dt, "project": self.name }) + task_doc.append('depends_on', {"task": dt_name}) + task_doc.save() def get_timeline_data(doctype, name): '''Return timeline for attendance''' diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 44c81a67c51..43240b20e72 100644 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -53,9 +53,9 @@ class Task(Document): frappe.throw(_("Progress % for a task cannot be more than 100.")) def update_depends_on(self): - depends_on_tasks = "" + depends_on_tasks = self.depends_on_tasks or "" for d in self.depends_on: - if d.task: + if d.task and not d.task in depends_on_tasks: depends_on_tasks += d.task + "," self.depends_on_tasks = depends_on_tasks diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 6f55a4411a3..08dd224fb14 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -7,7 +7,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ if(item.margin_type == "Percentage"){ item.total_margin = flt(item.price_list_rate) + flt(item.price_list_rate) * ( flt(item.margin_rate_or_amount) / 100); - }else{ + } else { item.total_margin = flt(item.price_list_rate) + flt(item.margin_rate_or_amount); } diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 7e0dccc6a1c..eec789a4ea8 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -260,6 +260,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ customer: me.frm.doc.customer, supplier: me.frm.doc.supplier, currency: me.frm.doc.currency, + update_stock: in_list(['Sales Invoice', 'Purchase Invoice'], me.frm.doc.doctype) ? cint(me.frm.doc.update_stock) : 0, conversion_rate: me.frm.doc.conversion_rate, price_list: me.frm.doc.selling_price_list || me.frm.doc.buying_price_list, @@ -274,14 +275,16 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ doctype: me.frm.doc.doctype, name: me.frm.doc.name, project: item.project || me.frm.doc.project, - qty: item.qty, - stock_qty: item.stock_qty + qty: item.qty || 1, + stock_qty: item.stock_qty, + conversion_factor: item.conversion_factor } }, callback: function(r) { if(!r.exc) { me.frm.script_manager.trigger("price_list_rate", cdt, cdn); + me.toggle_conversion_factor(item); } } }); @@ -566,11 +569,18 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ frappe.model.round_floats_in(item, ["qty", "conversion_factor"]); item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item)); refresh_field("stock_qty", item.name, item.parentfield); + this.toggle_conversion_factor(item); + this.apply_price_list(item, true); } }, + toggle_conversion_factor: function(item) { + // toggle read only property for conversion factor field if the uom and stock uom are same + this.frm.fields_dict.items.grid.toggle_enable("conversion_factor", + (item.uom != item.stock_uom)? true: false) + }, + qty: function(doc, cdt, cdn) { - this.apply_pricing_rule(frappe.get_doc(cdt, cdn), true); this.conversion_factor(doc, cdt, cdn); }, @@ -762,7 +772,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ "ignore_pricing_rule": me.frm.doc.ignore_pricing_rule, "doctype": me.frm.doc.doctype, "name": me.frm.doc.name, - "is_return": cint(me.frm.doc.is_return) + "is_return": cint(me.frm.doc.is_return), + "update_stock": in_list(['Sales Invoice', 'Purchase Invoice'], me.frm.doc.doctype) ? cint(me.frm.doc.update_stock) : 0, + "conversion_factor": me.frm.doc.conversion_factor }; }, @@ -781,7 +793,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ "parent": d.parent, "pricing_rule": d.pricing_rule, "warehouse": d.warehouse, - "serial_no": d.serial_no + "serial_no": d.serial_no, + "conversion_factor": d.conversion_factor || 1.0 }); // if doctype is Quotation Item / Sales Order Iten then add Margin Type and rate in item_list @@ -808,16 +821,13 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ for(var i=0, l=children.length; i 0) ? project[0].$input.val() : "" }; frappe.set_route("query-report", "General Ledger"); }, diff --git a/erpnext/public/js/templates/address_list.html b/erpnext/public/js/templates/address_list.html index f9a317f1737..02b44b83d6d 100644 --- a/erpnext/public/js/templates/address_list.html +++ b/erpnext/public/js/templates/address_list.html @@ -2,7 +2,7 @@
    {% for(var i=0, l=addr_list.length; i - {%= i+1 %}. {%= addr_list[i].address_type!="Other" ? addr_list[i].address_type : addr_list[i].address_title %} + {%= i+1 %}. {%= addr_list[i].address_type!="Other" ? __(addr_list[i].address_type) : addr_list[i].address_title %} {% if(addr_list[i].is_primary_address) { %} ({%= __("Primary") %}){% } %} {% if(addr_list[i].is_shipping_address) { %} diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 0d74d23eb4c..40ed6f98fe1 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -54,25 +54,30 @@ class TestCustomer(unittest.TestCase): self.assertEquals(value, details.get(key)) def test_rename(self): + # delete communication linked to these 2 customers for name in ("_Test Customer 1", "_Test Customer 1 Renamed"): frappe.db.sql("""delete from `tabCommunication` where communication_type='Comment' and reference_doctype=%s and reference_name=%s""", ("Customer", name)) + # add comments comment = frappe.get_doc("Customer", "_Test Customer 1").add_comment("Comment", "Test Comment for Rename") + # rename frappe.rename_doc("Customer", "_Test Customer 1", "_Test Customer 1 Renamed") + # check if customer renamed self.assertTrue(frappe.db.exists("Customer", "_Test Customer 1 Renamed")) self.assertFalse(frappe.db.exists("Customer", "_Test Customer 1")) - # test that comment gets renamed + # test that comment gets linked to renamed doc self.assertEquals(frappe.db.get_value("Communication", { "communication_type": "Comment", "reference_doctype": "Customer", "reference_name": "_Test Customer 1 Renamed" }), comment.name) + # rename back to original frappe.rename_doc("Customer", "_Test Customer 1 Renamed", "_Test Customer 1") def test_freezed_customer(self): diff --git a/erpnext/selling/doctype/quotation/test_records.json b/erpnext/selling/doctype/quotation/test_records.json index 8a37dbb7d72..5637fb906c1 100644 --- a/erpnext/selling/doctype/quotation/test_records.json +++ b/erpnext/selling/doctype/quotation/test_records.json @@ -23,9 +23,9 @@ "parentfield": "items", "qty": 10.0, "rate": 100.0, - "uom": "_Test UOM", - "stock_uom": "_Test UOM", - "conversion_factor": 1.0 + "uom": "_Test UOM 1", + "stock_uom": "_Test UOM 1", + "conversion_factor": 1.0 } ], "quotation_to": "Customer", diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json index cccef9c0437..3a7342738ae 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.json +++ b/erpnext/selling/doctype/quotation_item/quotation_item.json @@ -128,7 +128,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0, "width": "150px" @@ -813,6 +813,7 @@ "oldfieldtype": "Currency", "options": "currency", "permlevel": 0, + "precision": "9", "print_hide": 0, "print_hide_if_no_value": 0, "print_width": "100px", @@ -1483,7 +1484,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -1515,7 +1516,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0, "width": "150px" @@ -1532,7 +1533,7 @@ "istable": 1, "max_attachments": 0, "menu_index": 0, - "modified": "2017-03-30 19:35:44.119169", + "modified": "2017-04-18 18:47:26.869235", "modified_by": "Administrator", "module": "Selling", "name": "Quotation Item", diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 950150fd367..7621f4c1e3c 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -313,7 +313,7 @@ "remember_last_selected_value": 1, "report_hide": 0, "reqd": 1, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0, "width": "150px" @@ -377,7 +377,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0, "width": "160px" @@ -791,7 +791,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -821,7 +821,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -2321,7 +2321,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -2896,7 +2896,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0, "width": "150px" @@ -3516,7 +3516,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-04-10 12:13:03.136885", + "modified": "2017-04-18 20:02:54.895559", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 479f04e33f2..10096d0ae44 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -1397,7 +1397,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -1429,7 +1429,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -1871,7 +1871,7 @@ "istable": 1, "max_attachments": 0, "menu_index": 0, - "modified": "2017-03-30 16:18:49.367870", + "modified": "2017-04-18 18:43:47.150922", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 9d3bbf48f69..782996b4e63 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -282,7 +282,7 @@ "remember_last_selected_value": 1, "report_hide": 0, "reqd": 1, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0, "width": "150px" @@ -853,7 +853,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -883,7 +883,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -2514,7 +2514,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -2908,7 +2908,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -3288,7 +3288,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-04-10 12:03:29.645642", + "modified": "2017-04-18 18:41:53.065755", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 2649c49c08c..9738f9c24d4 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -1644,7 +1644,7 @@ "width": "120px" }, { - "allow_on_submit": 1, + "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, @@ -1894,7 +1894,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-04-05 23:26:03.390869", + "modified": "2017-04-19 11:53:42.656700", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/item/test_records.json b/erpnext/stock/doctype/item/test_records.json index aad8ed0fc27..2a1520e8ca3 100644 --- a/erpnext/stock/doctype/item/test_records.json +++ b/erpnext/stock/doctype/item/test_records.json @@ -24,6 +24,16 @@ "warehouse_reorder_qty": 20 } ], + "uoms": [ + { + "uom": "_Test UOM", + "conversion_factor": 1.0 + }, + { + "uom": "_Test UOM 1", + "conversion_factor": 10.0 + } + ], "stock_uom": "_Test UOM", "show_in_website": 1, "website_warehouse": "_Test Warehouse - _TC" diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 3877028378d..9cd512a1b6f 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -352,7 +352,7 @@ "remember_last_selected_value": 1, "report_hide": 0, "reqd": 1, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0, "width": "150px" @@ -2846,7 +2846,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-04-10 12:02:07.434102", + "modified": "2017-04-18 18:51:10.457027", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 91d1791cc77..fdc7f60d9b8 100755 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -1630,7 +1630,7 @@ "unique": 0 }, { - "allow_on_submit": 1, + "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, @@ -1804,7 +1804,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "unique": 0 }, @@ -1979,7 +1979,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-04-05 23:27:10.785444", + "modified": "2017-04-19 11:54:00.260885", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", @@ -1993,4 +1993,4 @@ "sort_order": "DESC", "track_changes": 0, "track_seen": 0 -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 6d99dc45c24..2f7779c1327 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -1040,7 +1040,7 @@ "unique": 0 }, { - "allow_on_submit": 1, + "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, @@ -1225,7 +1225,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-04-05 23:25:26.885642", + "modified": "2017-04-19 11:54:31.645381", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 3ada10ba6be..998a65a22aa 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -159,9 +159,8 @@ def get_basic_details(args, item): item.get("taxes")))), "uom": item.stock_uom, "min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "", - "conversion_factor": 1.0, "qty": args.qty or 1.0, - "stock_qty": 1.0, + "stock_qty": args.qty or 1.0, "price_list_rate": 0.0, "base_price_list_rate": 0.0, "rate": 0.0, @@ -172,10 +171,21 @@ def get_basic_details(args, item): "net_amount": 0.0, "discount_percentage": 0.0, "supplier": item.default_supplier, + "update_stock": args.get("update_stock") if args.get('doctype') in ['Sales Invoice', 'Purchase Invoice'] else 0, "delivered_by_supplier": item.delivered_by_supplier if args.get("doctype") in ["Sales Order", "Sales Invoice"] else 0, "is_fixed_asset": item.is_fixed_asset }) + # calculate conversion factor + if item.stock_uom == args.uom: + out.conversion_factor = 1.0 + else: + out.conversion_factor = args.conversion_factor or \ + get_conversion_factor(item.item_code, args.uom).get("conversion_factor") or 1.0 + + args.conversion_factor = out.conversion_factor + out.stock_qty = out.qty * out.conversion_factor + # if default specified in item is for another company, fetch from company for d in [["Account", "income_account", "default_income_account"], ["Account", "expense_account", "default_expense_account"], @@ -227,6 +237,8 @@ def get_price_list_rate(args, item_doc, out): out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) \ / flt(args.conversion_rate) + out.price_list_rate = flt(out.price_list_rate * (args.conversion_factor or 1.0)) + if not out.price_list_rate and args.transaction_type=="buying": from erpnext.stock.doctype.item.item import get_last_purchase_details out.update(get_last_purchase_details(item_doc.name, @@ -532,6 +544,9 @@ def get_serial_no(args): args = json.loads(args) args = frappe._dict(args) + if args.get('doctype') == 'Sales Invoice' and not args.get('update_stock'): + return "" + if args.get('warehouse') and args.get('stock_qty') and args.get('item_code'): if frappe.get_value('Item', {'item_code': args.item_code}, "has_serial_no") == 1: diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py index c0c39cf8de8..8766db3dfa6 100644 --- a/erpnext/templates/pages/product_search.py +++ b/erpnext/templates/pages/product_search.py @@ -20,10 +20,9 @@ def get_product_list(search=None, start=0, limit=12): query = """select name, item_name, item_code, route, website_image, thumbnail, item_group, description, web_long_description as website_description from `tabItem` - where show_in_website = 1 + where (show_in_website = 1 or show_variant_in_website = 1) and disabled=0 - and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %(today)s) - and (variant_of is null or variant_of = '')""" + and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %(today)s)""" # search term condition if search: