diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 97ab9343d2a..e88e00d9fc9 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe -__version__ = '7.1.23' +__version__ = '7.1.24' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index 8c7925a3ff4..1fbbf4fa91c 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -36,7 +36,7 @@ frappe.treeview_settings["Account"] = { description: __("Optional. This setting will be used to filter in various transactions.") }, {fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate'), - depends_on: 'eval:doc.is_group==1&&doc.account_type=="Tax"'}, + depends_on: 'eval:doc.is_group==0&&doc.account_type=="Tax"'}, {fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse", depends_on: 'eval:(!doc.is_group&&doc.account_type=="Stock")', get_query: function() { diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index b4c3349e11f..574103dc1bd 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -447,7 +447,7 @@ "collapsible": 0, "columns": 0, "fieldname": "payment_url", - "fieldtype": "Data", + "fieldtype": "Small Text", "hidden": 1, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -681,7 +681,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-11-07 05:55:42.339193", + "modified": "2016-12-12 13:30:42.858205", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Request", diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js index bbbab73e1c2..c1aa0c3cb52 100755 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.js +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js @@ -26,30 +26,6 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) { }); }); -frappe.ui.form.on("POS Profile", { - setup: function(frm) { - frm.trigger("get_query_for_groups") - }, - - get_query_for_groups: function(frm) { - frm.fields_dict['item_groups'].grid.get_field('item_group').get_query = function(frm, cdt, cdn) { - return{ - filters: { - 'is_group': 0 - } - } - } - - frm.fields_dict['customer_groups'].grid.get_field('customer_group').get_query = function(frm, cdt, cdn) { - return{ - filters: { - 'is_group': 0 - } - } - } - } -}) - // Income Account // -------------------------------- cur_frm.fields_dict['income_account'].get_query = function(doc,cdt,cdn) { diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 3476dddc0bb..3a418516417 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -138,6 +138,8 @@ def get_pricing_rule_for_item(args): }) if args.ignore_pricing_rule or not args.item_code: + if args.name and args.get("pricing_rule"): + item_details = remove_pricing_rule(args, item_details) return item_details if not (args.item_group and args.brand): @@ -178,9 +180,16 @@ def get_pricing_rule_for_item(args): else: item_details.discount_percentage = pricing_rule.discount_percentage elif args.get('pricing_rule'): - if frappe.db.get_value('Pricing Rule', args.get('pricing_rule'), 'price_or_discount') == 'Discount Percentage': - item_details.discount_percentage = 0.0 + item_details = remove_pricing_rule(args, item_details) + return item_details + +def remove_pricing_rule(args, item_details): + pricing_rule = frappe.db.get_value('Pricing Rule', args.get('pricing_rule'), ['price_or_discount', 'margin_type'], as_dict=1) + if pricing_rule and pricing_rule.price_or_discount == 'Discount Percentage': + item_details.discount_percentage = 0.0 + + if pricing_rule and pricing_rule.margin_type in ['Percentage', 'Amount']: item_details.margin_rate_or_amount = 0.0 item_details.margin_type = None diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 9fb11f2b7c2..3619e989d8c 100644 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -116,9 +116,9 @@ def get_items_list(pos_profile): item_groups = [] if pos_profile.get('item_groups'): # Get items based on the item groups defined in the POS profile - - cond = "item_group in (%s)"%(', '.join(['%s']*len(pos_profile.get('item_groups')))) - item_groups = [d.item_group for d in pos_profile.get('item_groups')] + for d in pos_profile.get('item_groups'): + item_groups.extend(get_child_nodes('Item Group', d.item_group)) + cond = "item_group in (%s)"%(', '.join(['%s']*len(item_groups))) return frappe.db.sql(""" select @@ -136,14 +136,19 @@ def get_customers_list(pos_profile): customer_groups = [] if pos_profile.get('customer_groups'): # Get customers based on the customer groups defined in the POS profile - - cond = "customer_group in (%s)"%(', '.join(['%s']*len(pos_profile.get('customer_groups')))) - customer_groups = [d.customer_group for d in pos_profile.get('customer_groups')] + for d in pos_profile.get('customer_groups'): + customer_groups.extend(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, territory from tabCustomer where disabled = 0 and {cond}""".format(cond=cond), tuple(customer_groups), as_dict=1) or {} +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)) + def get_serial_no_data(pos_profile, company): # get itemwise serial no data # example {'Nokia Lumia 1020': {'SN0001': 'Pune'}} @@ -241,8 +246,7 @@ def make_invoice(doc_list): for docs in doc_list: for name, doc in docs.items(): - if not frappe.db.exists('Sales Invoice', - {'offline_pos_name': name, 'docstatus': ("<", "2")}): + if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}): validate_records(doc) si_doc = frappe.new_doc('Sales Invoice') si_doc.offline_pos_name = name @@ -287,6 +291,7 @@ def submit_invoice(si_doc, name): try: si_doc.insert() si_doc.submit() + frappe.db.commit() except Exception, e: if frappe.message_log: frappe.message_log.pop() frappe.db.rollback() diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 95bea7a316b..ffd11365534 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -222,7 +222,6 @@ class SalarySlip(TransactionBase): ["date_of_joining", "relieving_date"]) holidays = self.get_holidays_for_employee(self.start_date, self.end_date) - working_days = date_diff(self.end_date, self.start_date) + 1 if not cint(frappe.db.get_value("HR Settings", None, "include_holidays_in_total_working_days")): working_days -= len(holidays) diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py index 9217c646b92..fe88d9afcfc 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py @@ -6,12 +6,15 @@ import frappe import unittest import erpnext from frappe.utils.make_random import get_random -from frappe.utils import nowdate, add_days, add_years +from frappe.utils import nowdate, add_days, add_years, getdate from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip -from erpnext.hr.doctype.salary_slip.test_salary_slip import make_earning_salary_component, make_deduction_salary_component +from erpnext.hr.doctype.salary_slip.test_salary_slip \ + import make_earning_salary_component, make_deduction_salary_component + +test_dependencies = ["Fiscal Year"] class TestSalaryStructure(unittest.TestCase): - def test_setup(self): + def setUp(self): self.make_holiday_list() frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Structure Test Holiday List") make_earning_salary_component(["Basic Salary", "Allowance", "HRA"]) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 213199fea4c..aad8d421b44 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -356,4 +356,5 @@ erpnext.patches.v7_1.update_bom_base_currency erpnext.patches.v7_0.update_status_of_po_so erpnext.patches.v7_1.set_budget_against_as_cost_center erpnext.patches.v7_1.set_currency_exchange_date -erpnext.patches.v7_1.set_sales_person_status \ No newline at end of file +erpnext.patches.v7_1.set_sales_person_status +erpnext.patches.v7_1.repost_stock_for_deleted_bins_for_merging_items diff --git a/erpnext/patches/v7_1/repost_stock_for_deleted_bins_for_merging_items.py b/erpnext/patches/v7_1/repost_stock_for_deleted_bins_for_merging_items.py new file mode 100644 index 00000000000..5c63c007adf --- /dev/null +++ b/erpnext/patches/v7_1/repost_stock_for_deleted_bins_for_merging_items.py @@ -0,0 +1,41 @@ +from __future__ import unicode_literals +import frappe +from erpnext.stock.stock_balance import repost_stock + +def execute(): + modified_items = frappe.db.sql_list(""" + select name from `tabItem` + where is_stock_item=1 and modified >= '2016-10-31' + """) + + if not modified_items: + return + + item_warehouses_with_transactions = [] + transactions = ("Sales Order Item", "Material Request Item", "Purchase Order Item", + "Stock Ledger Entry", "Packed Item") + + for doctype in transactions: + item_warehouses_with_transactions += list(frappe.db.sql(""" + select distinct item_code, warehouse + from `tab{0}` where docstatus=1 and item_code in ({1})""" + .format(doctype, ', '.join(['%s']*len(modified_items))), tuple(modified_items))) + + item_warehouses_with_transactions += list(frappe.db.sql(""" + select distinct production_item, fg_warehouse + from `tabProduction Order` where docstatus=1 and production_item in ({0})""" + .format(', '.join(['%s']*len(modified_items))), tuple(modified_items))) + + item_warehouses_with_transactions += list(frappe.db.sql(""" + select distinct pr_item.item_code, pr.source_warehouse + from `tabProduction Order` pr, `tabProduction Order Item` pr_item + where pr_item.parent and pr.name and pr.docstatus=1 and pr_item.item_code in ({0})""" + .format(', '.join(['%s']*len(modified_items))), tuple(modified_items))) + + item_warehouses_with_bin = list(frappe.db.sql("select distinct item_code, warehouse from `tabBin`")) + + item_warehouses_with_missing_bin = list( + set(item_warehouses_with_transactions) - set(item_warehouses_with_bin)) + + for item_code, warehouse in item_warehouses_with_missing_bin: + repost_stock(item_code, warehouse) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index bad841661ab..5d2d557d0f7 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -343,7 +343,7 @@ "columns": 0, "depends_on": "eval:(doc.__islocal&&doc.is_stock_item && !doc.has_serial_no && !doc.has_batch_no)", "fieldname": "opening_stock", - "fieldtype": "Int", + "fieldtype": "Float", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -2683,7 +2683,7 @@ "issingle": 0, "istable": 0, "max_attachments": 1, - "modified": "2016-11-18 02:19:54.507883", + "modified": "2016-12-13 02:19:54.507883", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 3d248d1c280..98d0ebcfac2 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -532,8 +532,6 @@ class Item(WebsiteGenerator): frappe.throw(_("To merge, following properties must be same for both items") + ": \n" + ", ".join([self.meta.get_label(fld) for fld in field_list])) - frappe.db.sql("delete from `tabBin` where item_code=%s", old_name) - def after_rename(self, old_name, new_name, merge): if self.route: invalidate_cache_for_item(self) @@ -567,8 +565,14 @@ class Item(WebsiteGenerator): existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock") frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) - for warehouse in frappe.db.sql("select warehouse from `tabBin` where item_code=%s", new_name): - repost_stock(new_name, warehouse[0]) + repost_stock_for_warehouses = frappe.db.sql_list("""select distinct warehouse + from tabBin where item_code=%s""", new_name) + + # Delete all existing bins to avoid duplicate bins for the same item and warehouse + frappe.db.sql("delete from `tabBin` where item_code=%s", new_name) + + for warehouse in repost_stock_for_warehouses: + repost_stock(new_name, warehouse) frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock) frappe.db.auto_commit_on_many_writes = 0 diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index aceefc0ce5f..706fd5a1f46 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -9,6 +9,9 @@ from frappe.test_runner import make_test_records from erpnext.controllers.item_variant import (create_variant, ItemVariantExistsError, InvalidItemAttributeValueError) +from frappe.model.rename_doc import rename_doc +from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry + test_ignore = ["BOM"] test_dependencies = ["Warehouse"] @@ -164,11 +167,31 @@ class TestItem(unittest.TestCase): variant.item_name = "_Test Numeric Variant Large 1.1m" self.assertRaises(InvalidItemAttributeValueError, variant.save) - variant = create_variant("_Test Numeric Template Item", {"Test Size": "Large", "Test Item Length": 1.5}) + variant = create_variant("_Test Numeric Template Item", + {"Test Size": "Large", "Test Item Length": 1.5}) self.assertEquals(variant.item_code, None) variant.item_code = "_Test Numeric Variant-L-1.5" variant.item_name = "_Test Numeric Variant Large 1.5m" variant.save() + + def test_item_merging(self): + create_item("Test Item for Merging 1") + create_item("Test Item for Merging 2") + + make_stock_entry(item_code="Test Item for Merging 1", target="_Test Warehouse - _TC", + qty=1, rate=100) + make_stock_entry(item_code="Test Item for Merging 2", target="_Test Warehouse 1 - _TC", + qty=1, rate=100) + + rename_doc("Item", "Test Item for Merging 1", "Test Item for Merging 2", merge=True) + + self.assertFalse(frappe.db.exists("Item", "Test Item for Merging 1")) + + self.assertTrue(frappe.db.get_value("Bin", + {"item_code": "Test Item for Merging 2", "warehouse": "_Test Warehouse - _TC"})) + + self.assertTrue(frappe.db.get_value("Bin", + {"item_code": "Test Item for Merging 2", "warehouse": "_Test Warehouse 1 - _TC"})) def make_item_variant(): if not frappe.db.exists("Item", "_Test Variant Item-S"): @@ -184,3 +207,14 @@ def get_total_projected_qty(item): return total_qty[0].projected_qty if total_qty else 0.0 test_records = frappe.get_test_records('Item') + +def create_item(item_code, is_stock_item=None): + if not frappe.db.exists("Item", item_code): + item = frappe.new_doc("Item") + item.item_code = item_code + item.item_name = item_code + item.description = item_code + item.item_group = "All Item Groups" + item.is_stock_item = is_stock_item or 1 + item.save() + \ No newline at end of file diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.py b/erpnext/stock/doctype/warehouse/test_warehouse.py index 223258430f4..edc54005479 100644 --- a/erpnext/stock/doctype/warehouse/test_warehouse.py +++ b/erpnext/stock/doctype/warehouse/test_warehouse.py @@ -1,7 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals - +from frappe.model.rename_doc import rename_doc +from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry +from frappe.utils import cint +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory import frappe import unittest @@ -21,5 +24,77 @@ class TestWarehouse(unittest.TestCase): for child_warehouse in child_warehouses: self.assertEquals(p_warehouse.name, child_warehouse.parent_warehouse) self.assertEquals(child_warehouse.is_group, 0) + + def test_warehouse_renaming(self): + set_perpetual_inventory(1) + create_warehouse("Test Warehouse for Renaming 1") + self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Renaming 1 - _TC")) + self.assertTrue(frappe.db.get_value("Account", + filters={"warehouse": "Test Warehouse for Renaming 1 - _TC"})) + # Rename with abbr + if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 2 - _TC"): + frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC") + rename_doc("Warehouse", "Test Warehouse for Renaming 1 - _TC", "Test Warehouse for Renaming 2 - _TC") + + self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Renaming 2 - _TC")) + self.assertTrue(frappe.db.get_value("Account", + filters={"warehouse": "Test Warehouse for Renaming 2 - _TC"})) + + # Rename without abbr + if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 3 - _TC"): + frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC") + + rename_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC", "Test Warehouse for Renaming 3") + + self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Renaming 3 - _TC")) + self.assertTrue(frappe.db.get_value("Account", + filters={"warehouse": "Test Warehouse for Renaming 3 - _TC"})) + + set_perpetual_inventory(0) + + def test_warehouse_merging(self): + set_perpetual_inventory(1) + + create_warehouse("Test Warehouse for Merging 1") + create_warehouse("Test Warehouse for Merging 2") + + make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 1 - _TC", + qty=1, rate=100) + make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 2 - _TC", + qty=1, rate=100) + + existing_bin_qty = ( + cint(frappe.db.get_value("Bin", + {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 1 - _TC"}, "actual_qty")) + + cint(frappe.db.get_value("Bin", + {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty")) + ) + + rename_doc("Warehouse", "Test Warehouse for Merging 1 - _TC", + "Test Warehouse for Merging 2 - _TC", merge=True) + + self.assertFalse(frappe.db.exists("Warehouse", "Test Warehouse for Merging 1 - _TC")) + + bin_qty = frappe.db.get_value("Bin", + {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty") + + self.assertEqual(bin_qty, existing_bin_qty) + + self.assertFalse(frappe.db.exists("Account", "Test Warehouse for Merging 1 - _TC")) + self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Merging 2 - _TC")) + self.assertTrue(frappe.db.get_value("Account", + filters={"warehouse": "Test Warehouse for Merging 2 - _TC"})) + + set_perpetual_inventory(0) + +def create_warehouse(warehouse_name): + if not frappe.db.exists("Warehouse", warehouse_name + " - _TC"): + w = frappe.new_doc("Warehouse") + w.warehouse_name = warehouse_name + w.parent_warehouse = "_Test Warehouse Group - _TC" + w.company = "_Test Company" + w.save() + + \ No newline at end of file diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index 5d0951bf32d..e01cc0b6fe2 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -143,9 +143,7 @@ class Warehouse(NestedSet): if self.company != frappe.db.get_value("Warehouse", new_warehouse, "company"): frappe.throw(_("Both Warehouse must belong to same Company")) - frappe.db.sql("delete from `tabBin` where warehouse=%s", olddn) - - self.rename_account_for(olddn, newdn, merge) + self.rename_account_for(olddn, new_warehouse, merge) return new_warehouse @@ -195,8 +193,14 @@ class Warehouse(NestedSet): existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock") frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) - for item in frappe.db.sql(""" select distinct item_code from tabBin where warehouse = %s""", newdn): - repost_stock(item[0], newdn) + repost_stock_for_items = frappe.db.sql_list("""select distinct item_code + from tabBin where warehouse=%s""", newdn) + + # Delete all existing bins to avoid duplicate bins for the same item and warehouse + frappe.db.sql("delete from `tabBin` where warehouse=%s", newdn) + + for item_code in repost_stock_for_items: + repost_stock(item_code, newdn) frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock) frappe.db.auto_commit_on_many_writes = 0 diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 16ea58e47ab..7bcad08e89e 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -172,7 +172,7 @@ def get_basic_details(args, item): "net_amount": 0.0, "discount_percentage": 0.0, "supplier": item.default_supplier, - "delivered_by_supplier": item.delivered_by_supplier, + "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 }) diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.json b/erpnext/support/doctype/warranty_claim/warranty_claim.json index 4e63bb64bf2..d1b0b8f85cd 100644 --- a/erpnext/support/doctype/warranty_claim/warranty_claim.json +++ b/erpnext/support/doctype/warranty_claim/warranty_claim.json @@ -1124,7 +1124,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-11-07 05:06:08.638760", + "modified": "2016-12-13 11:17:40.538428", "modified_by": "Administrator", "module": "Support", "name": "Warranty Claim",