From ab842541887cc8c0cda245cef88940e911731b97 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 26 Apr 2018 19:18:29 +0530 Subject: [PATCH] Asset linked to purchase receipt and serial no --- .../purchase_invoice/purchase_invoice.py | 17 -- .../purchase_invoice_item.json | 2 +- erpnext/assets/doctype/asset/asset.js | 3 +- erpnext/assets/doctype/asset/asset.json | 109 ++++++++++- erpnext/assets/doctype/asset/asset.py | 25 ++- .../assets/doctype/asset/asset_dashboard.py | 7 + .../asset_maintenance/asset_maintenance.json | 49 ++++- .../asset_maintenance/asset_maintenance.py | 3 + .../asset_movement/asset_movement.json | 171 +++++++++++++++++- .../doctype/asset_movement/asset_movement.py | 17 +- erpnext/controllers/accounts_controller.py | 52 +++--- erpnext/controllers/buying_controller.py | 126 +++++++++++++ erpnext/setup/doctype/company/company.js | 8 + erpnext/stock/doctype/item/item.json | 12 +- erpnext/stock/doctype/item/item.py | 2 +- .../purchase_receipt/purchase_receipt.js | 14 ++ .../purchase_receipt/purchase_receipt.py | 2 + .../purchase_receipt_dashboard.py | 3 +- .../purchase_receipt/test_purchase_receipt.py | 42 ++++- .../purchase_receipt_item.json | 66 ++++++- .../stock/doctype/serial_no/serial_no.json | 78 +++++++- erpnext/stock/doctype/serial_no/serial_no.py | 56 +++--- erpnext/stock/get_item_details.py | 4 + 23 files changed, 763 insertions(+), 105 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 67b41a3248..d2cc4eea9f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -305,24 +305,8 @@ class PurchaseInvoice(BuyingController): self.make_gl_entries() self.update_project() - self.update_fixed_asset() update_linked_invoice(self.doctype, self.name, self.inter_company_invoice_reference) - def update_fixed_asset(self): - for d in self.get("items"): - if d.is_fixed_asset: - asset = frappe.get_doc("Asset", d.asset) - if self.docstatus==1: - asset.purchase_invoice = self.name - asset.purchase_date = self.posting_date - asset.supplier = self.supplier - else: - asset.purchase_invoice = None - asset.supplier = None - - asset.flags.ignore_validate_update_after_submit = True - asset.save() - def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False): if not self.grand_total: return @@ -636,7 +620,6 @@ class PurchaseInvoice(BuyingController): self.make_gl_entries_on_cancel() self.update_project() - self.update_fixed_asset() frappe.db.set(self, 'status', 'Cancelled') unlink_inter_company_invoice(self.doctype, self.name, self.inter_company_invoice_reference) 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 d1f99ab3eb..ef9b2f69b6 100755 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -2258,7 +2258,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-02-22 15:15:25.297672", + "modified": "2018-04-23 14:07:33.576495", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index f4a01aefca..edc1e364f5 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -181,7 +181,8 @@ frappe.ui.form.on('Asset', { args: { "asset": frm.doc.name, "item_code": frm.doc.item_code, - "company": frm.doc.company + "company": frm.doc.company, + "serial_no": frm.doc.serial_no }, method: "erpnext.assets.doctype.asset.asset.make_sales_invoice", callback: function(r) { diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 49b574d3b0..63b9167cc6 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -40,6 +40,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -71,6 +72,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -102,6 +104,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -133,6 +136,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -165,6 +169,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -196,6 +201,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -228,6 +234,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -260,6 +267,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -292,6 +300,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -322,6 +331,38 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "serial_no", + "fieldtype": "Small Text", + "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": "Serial No", + "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, + "translatable": 0, "unique": 0 }, { @@ -351,6 +392,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -382,6 +424,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -413,6 +456,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -444,6 +488,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -474,6 +519,39 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "purchase_receipt", + "fieldtype": "Link", + "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": "Purchase Receipt", + "length": 0, + "no_copy": 1, + "options": "Purchase Receipt", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "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, + "translatable": 0, "unique": 0 }, { @@ -505,6 +583,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -535,6 +614,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -565,6 +645,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -595,6 +676,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -626,6 +708,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -655,6 +738,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -686,6 +770,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -715,6 +800,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -747,6 +833,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -779,6 +866,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -808,6 +896,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -838,6 +927,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -869,6 +959,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -902,6 +993,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -933,6 +1025,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -963,6 +1056,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -992,6 +1086,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1023,6 +1118,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1053,6 +1149,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1084,6 +1181,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1115,6 +1213,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1146,6 +1245,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1176,6 +1276,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1207,6 +1308,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1237,6 +1339,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -1251,7 +1354,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-01-05 09:53:05.945328", + "modified": "2018-04-19 20:03:13.669957", "modified_by": "Administrator", "module": "Assets", "name": "Asset", @@ -1260,7 +1363,6 @@ "permissions": [ { "amend": 1, - "apply_user_permissions": 0, "cancel": 1, "create": 1, "delete": 1, @@ -1280,7 +1382,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 1, "create": 1, "delete": 1, @@ -1307,4 +1408,4 @@ "sort_order": "DESC", "track_changes": 0, "track_seen": 0 -} +} \ No newline at end of file diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index baffdd757d..a9cc924b8f 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -10,6 +10,7 @@ from frappe.model.document import Document from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_fixed_asset_account from erpnext.assets.doctype.asset.depreciation \ import get_disposal_account_and_cost_center, get_depreciation_accounts +from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos class Asset(Document): def validate(self): @@ -26,12 +27,16 @@ class Asset(Document): def on_submit(self): self.set_status() + self.update_stock_movement() def on_cancel(self): self.validate_cancellation() self.delete_depreciation_entries() self.set_status() + def on_update(self): + self.update_serial_nos() + def validate_item(self): item = frappe.db.get_value("Item", self.item_code, ["is_fixed_asset", "is_stock_item", "disabled"], as_dict=1) @@ -219,6 +224,9 @@ class Asset(Document): if self.purchase_invoice: frappe.throw(_("Please cancel Purchase Invoice {0} first").format(self.purchase_invoice)) + if self.purchase_receipt: + frappe.throw(_("Please cancel Purchase Receipt {0} first").format(self.purchase_receipt)) + def delete_depreciation_entries(self): for d in self.get("schedules"): if d.journal_entry: @@ -250,6 +258,20 @@ class Asset(Document): status = "Cancelled" return status + def update_serial_nos(self): + if self.serial_no: + serial_nos = get_serial_nos(self.serial_no) + frappe.db.sql(""" update `tabSerial No` set asset = '%s' where + name in(%s)"""%(self.name, ','.join(['%s'] * len(serial_nos))), tuple(serial_nos)) + + def update_stock_movement(self): + asset_movement = frappe.db.get_value('Asset Movement', + {'asset': self.name, 'reference_name': self.purchase_receipt, 'docstatus': 0}, 'name') + + if asset_movement: + doc = frappe.get_doc('Asset Movement', asset_movement) + doc.submit() + def update_maintenance_status(): assets = frappe.get_all('Asset', filters = {'docstatus': 1, 'maintenance_required': 1}) @@ -280,7 +302,7 @@ def make_purchase_invoice(asset, item_code, gross_purchase_amount, company, post return pi @frappe.whitelist() -def make_sales_invoice(asset, item_code, company): +def make_sales_invoice(asset, item_code, company, serial_no): si = frappe.new_doc("Sales Invoice") si.company = company si.currency = frappe.db.get_value("Company", company, "default_currency") @@ -290,6 +312,7 @@ def make_sales_invoice(asset, item_code, company): "is_fixed_asset": 1, "asset": asset, "income_account": disposal_account, + "serial_no": serial_no, "cost_center": depreciation_cost_center, "qty": 1 }) diff --git a/erpnext/assets/doctype/asset/asset_dashboard.py b/erpnext/assets/doctype/asset/asset_dashboard.py index 94dad1b311..89699f3edb 100644 --- a/erpnext/assets/doctype/asset/asset_dashboard.py +++ b/erpnext/assets/doctype/asset/asset_dashboard.py @@ -1,6 +1,9 @@ def get_data(): return { 'fieldname': 'asset_name', + 'non_standard_fieldnames': { + 'Asset Movement': 'asset' + }, 'transactions': [ { 'label': ['Maintenance'], @@ -9,6 +12,10 @@ def get_data(): { 'label': ['Repair'], 'items': ['Asset Repair'] + }, + { + 'label': ['Movement'], + 'items': ['Asset Movement'] } ] } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json index 72d96b0db0..f36fe4e078 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json @@ -42,6 +42,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -73,6 +74,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -104,6 +106,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -135,6 +138,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -164,6 +168,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -195,6 +200,39 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "serial_no", + "fieldtype": "Small Text", + "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": "Serial No", + "length": 0, + "no_copy": 0, + "options": "asset_name.serial_no", + "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, + "translatable": 0, "unique": 0 }, { @@ -224,6 +262,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -255,6 +294,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -284,6 +324,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -315,6 +356,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -346,6 +388,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -376,6 +419,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -407,6 +451,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -420,7 +465,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-12-01 15:13:29.816396", + "modified": "2018-04-20 08:39:27.072622", "modified_by": "Administrator", "module": "Assets", "name": "Asset Maintenance", @@ -429,7 +474,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -449,7 +493,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index 7551eae229..b30685f108 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -11,6 +11,9 @@ from frappe.utils import add_days, add_months, add_years, getdate, nowdate class AssetMaintenance(Document): def validate(self): + if not self.serial_no: + self.serial_no = frappe.db.get_value("Asset", self.asset_name, 'serial_no') + for task in self.get('asset_maintenance_tasks'): if task.end_date and (getdate(task.start_date) >= getdate(task.end_date)): throw(_("Start date should be less than end date for task {0}").format(task.maintenance_task)) diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.json b/erpnext/assets/doctype/asset_movement/asset_movement.json index 0c05552962..3c3a1dc9cd 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.json +++ b/erpnext/assets/doctype/asset_movement/asset_movement.json @@ -12,6 +12,39 @@ "document_type": "", "editable_grid": 0, "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Transfer", + "fieldname": "purpose", + "fieldtype": "Select", + "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": "Purpose", + "length": 0, + "no_copy": 0, + "options": "Receipt\nTransfer", + "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, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -41,6 +74,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -71,6 +105,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -102,6 +137,38 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "serial_no", + "fieldtype": "Small Text", + "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": "Serial No", + "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, + "translatable": 0, "unique": 0 }, { @@ -131,6 +198,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -162,6 +230,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -193,6 +262,102 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "reference", + "fieldtype": "Section Break", + "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": "Reference", + "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, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "reference_doctype", + "fieldtype": "Link", + "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": "Reference DocType", + "length": 0, + "no_copy": 1, + "options": "DocType", + "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, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "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": "Reference Name", + "length": 0, + "no_copy": 1, + "options": "reference_doctype", + "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, + "translatable": 0, "unique": 0 }, { @@ -223,6 +388,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -236,7 +402,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-10-19 16:08:17.389257", + "modified": "2018-04-20 15:45:54.156501", "modified_by": "Administrator", "module": "Assets", "name": "Asset Movement", @@ -245,7 +411,6 @@ "permissions": [ { "amend": 1, - "apply_user_permissions": 0, "cancel": 1, "create": 1, "delete": 1, @@ -265,7 +430,6 @@ }, { "amend": 1, - "apply_user_permissions": 0, "cancel": 1, "create": 1, "delete": 1, @@ -285,7 +449,6 @@ }, { "amend": 1, - "apply_user_permissions": 0, "cancel": 1, "create": 1, "delete": 1, diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py index 574c49992b..42ed249959 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/asset_movement.py @@ -11,19 +11,22 @@ class AssetMovement(Document): def validate(self): self.validate_asset() self.validate_warehouses() - + def validate_asset(self): - status, company = frappe.db.get_value("Asset", self.asset, ["status", "company"]) - if status in ("Draft", "Scrapped", "Sold"): + status, company, serial_no = frappe.db.get_value("Asset", self.asset, ["status", "company", "serial_no"]) + if self.purpose == 'Transfer' and status in ("Draft", "Scrapped", "Sold"): frappe.throw(_("{0} asset cannot be transferred").format(status)) - + if company != self.company: frappe.throw(_("Asset {0} does not belong to company {1}").format(self.asset, self.company)) - + + if serial_no and not self.serial_no: + self.serial_no = serial_no + def validate_warehouses(self): - if not self.source_warehouse: + if self.purpose == 'Transfer' and not self.source_warehouse: self.source_warehouse = frappe.db.get_value("Asset", self.asset, "warehouse") - + if self.source_warehouse == self.target_warehouse: frappe.throw(_("Source and Target Warehouse cannot be same")) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index c70cfcd811..40028afb01 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -600,40 +600,36 @@ class AccountsController(TransactionBase): if d.qty > 1: frappe.throw(_("Row #{0}: Qty must be 1, as item is a fixed asset. Please use separate row for multiple qty.").format(d.idx)) - if d.meta.get_field("asset"): - if not d.asset: - frappe.throw(_("Row #{0}: Asset is mandatory for fixed asset purchase/sale") - .format(d.idx)) - else: - asset = frappe.get_doc("Asset", d.asset) + if d.meta.get_field("asset") and d.asset: + asset = frappe.get_doc("Asset", d.asset) - if asset.company != self.company: - frappe.throw(_("Row #{0}: Asset {1} does not belong to company {2}") - .format(d.idx, d.asset, self.company)) + if asset.company != self.company: + frappe.throw(_("Row #{0}: Asset {1} does not belong to company {2}") + .format(d.idx, d.asset, self.company)) - elif asset.item_code != d.item_code: - frappe.throw(_("Row #{0}: Asset {1} does not linked to Item {2}") - .format(d.idx, d.asset, d.item_code)) + elif asset.item_code != d.item_code: + frappe.throw(_("Row #{0}: Asset {1} does not linked to Item {2}") + .format(d.idx, d.asset, d.item_code)) - elif asset.docstatus != 1: - frappe.throw(_("Row #{0}: Asset {1} must be submitted").format(d.idx, d.asset)) + elif asset.docstatus != 1: + frappe.throw(_("Row #{0}: Asset {1} must be submitted").format(d.idx, d.asset)) - elif self.doctype == "Purchase Invoice": - if asset.status != "Submitted": - frappe.throw(_("Row #{0}: Asset {1} is already {2}") - .format(d.idx, d.asset, asset.status)) - elif getdate(asset.purchase_date) != getdate(self.posting_date): - frappe.throw(_("Row #{0}: Posting Date must be same as purchase date {1} of asset {2}").format(d.idx, asset.purchase_date, d.asset)) - elif asset.is_existing_asset: - frappe.throw(_("Row #{0}: Purchase Invoice cannot be made against an existing asset {1}").format(d.idx, d.asset)) + elif self.doctype == "Purchase Invoice": + if asset.status != "Submitted": + frappe.throw(_("Row #{0}: Asset {1} is already {2}") + .format(d.idx, d.asset, asset.status)) + elif getdate(asset.purchase_date) != getdate(self.posting_date): + frappe.throw(_("Row #{0}: Posting Date must be same as purchase date {1} of asset {2}").format(d.idx, asset.purchase_date, d.asset)) + elif asset.is_existing_asset: + frappe.throw(_("Row #{0}: Purchase Invoice cannot be made against an existing asset {1}").format(d.idx, d.asset)) - elif self.docstatus=="Sales Invoice" and self.docstatus == 1: - if self.update_stock: - frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale")) + elif self.docstatus=="Sales Invoice" and self.docstatus == 1: + if self.update_stock: + frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale")) - elif asset.status in ("Scrapped", "Cancelled", "Sold"): - frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}") - .format(d.idx, d.asset, asset.status)) + elif asset.status in ("Scrapped", "Cancelled", "Sold"): + frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}") + .format(d.idx, d.asset, asset.status)) def delink_advance_entries(self, linked_doc_name): total_allocated_amount = 0 diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index de6ed79351..c340901e3b 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -11,6 +11,7 @@ from erpnext.stock.get_item_details import get_conversion_factor from erpnext.buying.utils import validate_for_items, update_last_purchase_rate from erpnext.stock.stock_ledger import get_valuation_rate from erpnext.stock.doctype.stock_entry.stock_entry import get_used_alternative_items +from erpnext.stock.doctype.serial_no.serial_no import get_auto_serial_nos, auto_make_serial_nos from erpnext.controllers.stock_controller import StockController @@ -439,6 +440,11 @@ class BuyingController(StockController): if self.get('is_return'): return + if self.doctype in ['Purchase Receipt', 'Purchase Invoice']: + if self.doctype == 'Purchase Receipt': + self.process_fixed_asset() + self.update_fixed_asset() + update_last_purchase_rate(self, is_submit = 1) def on_cancel(self): @@ -446,6 +452,118 @@ class BuyingController(StockController): return update_last_purchase_rate(self, is_submit = 0) + if self.doctype in ['Purchase Receipt', 'Purchase Invoice']: + if self.doctype == 'Purchase Receipt': + self.delete_linked_asset() + self.update_fixed_asset() + + def process_fixed_asset(self): + if not self.doctype in ['Purchase Receipt', 'Purchase Invoice']: + return + + asset_items = [d.item_code for d in self.items if d.is_fixed_asset] + if asset_items: + self.make_serial_nos_for_asset(asset_items) + + def make_serial_nos_for_asset(self, asset_items): + items_data = get_asset_item_details(asset_items) + + for d in self.items: + if d.is_fixed_asset: + item_data = items_data.get(d.item_code) + + if item_data.get('has_serial_no'): + # If item has serial no + if item_data.get('serial_no_series') and not d.serial_no: + serial_nos = get_auto_serial_nos(item_data.get('serial_no_series'), d.qty) + elif d.serial_no: + serial_nos = d.serial_no + elif not d.serial_no: + frappe.throw(_("Serial no is mandatory for the item {0}").format(d.item_code)) + + auto_make_serial_nos({ + 'serial_no': serial_nos, + 'item_code': d.item_code, + 'via_stock_ledger': False, + 'company': self.company, + 'actual_qty': d.qty, + 'purchase_document_type': self.doctype, + 'purchase_document_no': self.name + }) + d.db_set('serial_no', serial_nos) + + if not d.asset: + asset = self.make_asset(d) + d.db_set('asset', asset) + + if d.asset: + self.make_asset_movement(d) + + def make_asset(self, row): + asset = frappe.get_doc({ + 'doctype': 'Asset', + 'item_code': row.item_code, + 'asset_name': '{0} - {1}'.format(self.name, row.item_code), + 'warehouse': row.warehouse, + 'serial_no': row.serial_no, + 'company': self.company, + 'purchase_date': self.posting_date, + 'purchase_receipt': self.name if self.doctype == 'Purchase Receipt' else None, + 'purchase_invoice': self.name if self.doctype == 'Purchase Invoice' else None + }) + + asset.flags.ignore_validate = True + asset.flags.ignore_mandatory = True + asset.insert() + + frappe.msgprint(_("Asset {0} created").format(asset.name)) + return asset.name + + def make_asset_movement(self, row): + asset_movement = frappe.get_doc({ + 'doctype': 'Asset Movement', + 'asset': row.asset, + 'source_warehouse': '', + 'target_warehouse': row.warehouse, + 'purpose': 'Receipt', + 'serial_no': row.serial_no, + 'company': self.company, + 'transaction_date': self.posting_date, + 'reference_doctype': self.doctype, + 'reference_name': self.name + }).insert() + + return asset_movement.name + + def update_fixed_asset(self): + field = 'purchase_invoice' if self.doctype == 'Purchase Invoice' else 'purchase_receipt' + + for d in self.get("items"): + if d.is_fixed_asset and d.asset: + asset = frappe.get_doc("Asset", d.asset) + if self.docstatus in [0, 1] and not asset.get(field): + asset.set(field, self.name) + asset.purchase_date = self.posting_date + asset.supplier = self.supplier + else: + asset.set(field, None) + asset.supplier = None + + asset.flags.ignore_validate_update_after_submit = True + if asset.docstatus == 0: + asset.flags.ignore_validate = True + + asset.save() + + def delete_linked_asset(self): + if not self.doctype in ['Purchase Receipt', 'Purchase Invoice']: + return + + if self.doctype == 'Purchase Invoice' and self.get('update_stock'): + return + + frappe.db.sql("delete from `tabAsset Movement` where reference_name=%s and docstatus = 0", self.name) + frappe.db.sql("delete from `tabSerial No` where purchase_document_no=%s", self.name) def validate_schedule_date(self): if not self.schedule_date: @@ -480,3 +598,11 @@ def get_items_from_bom(item_code, bom, exploded_item=1): msgprint(_("Specified BOM {0} does not exist for Item {1}").format(bom, item_code), raise_exception=1) return bom_items + +def get_asset_item_details(asset_items): + asset_items_data = {} + for d in frappe.get_all('Item', fields = ["name", "has_serial_no", "serial_no_series"], + filters = {'name': ('in', asset_items)}): + asset_items_data.setdefault(d.name, d) + + return asset_items_data diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index e164d6949f..d4095772cd 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -5,6 +5,14 @@ frappe.provide("erpnext.company"); frappe.ui.form.on("Company", { setup: function(frm) { + frm.fields_dict.fixed_asset_account.get_query = function() { + return { + filters: { + account_type: "Fixed Asset", + company: frm.doc.name + } + } + } erpnext.company.setup_queries(frm); }, diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 21900ad440..251717e127 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -1285,7 +1285,7 @@ "collapsible": 1, "collapsible_depends_on": "eval:doc.has_batch_no || doc.has_serial_no", "columns": 0, - "depends_on": "is_stock_item", + "depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset", "fieldname": "serial_nos_and_batches", "fieldtype": "Section Break", "hidden": 0, @@ -1513,7 +1513,7 @@ "collapsible": 0, "columns": 0, "default": "", - "depends_on": "eval:doc.is_stock_item", + "depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset", "description": "", "fieldname": "has_serial_no", "fieldtype": "Check", @@ -3725,7 +3725,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -3745,7 +3744,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -3765,7 +3763,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -3785,7 +3782,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -3805,7 +3801,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -3825,7 +3820,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -3845,7 +3839,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -3865,7 +3858,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 123e73f652..fafdaab51f 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -444,7 +444,7 @@ class Item(WebsiteGenerator): _("Conversion factor for default Unit of Measure must be 1 in row {0}").format(d.idx)) def validate_item_type(self): - if self.has_serial_no == 1 and self.is_stock_item == 0: + if self.has_serial_no == 1 and self.is_stock_item == 0 and not self.is_fixed_asset: msgprint(_("'Has Serial No' can not be 'Yes' for non-stock item"), raise_exception=1) if self.has_serial_no == 0 and self.serial_no_series: diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index 791b2532f7..69d2f2a3d7 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -65,6 +65,20 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend if (erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) { this.show_general_ledger(); } + + this.frm.add_custom_button(__('Asset'), function() { + frappe.route_options = { + purchase_receipt: me.frm.doc.name, + }; + frappe.set_route("List", "Asset"); + }, __("View")); + + this.frm.add_custom_button(__('Asset Movement'), function() { + frappe.route_options = { + reference_name: me.frm.doc.name, + }; + frappe.set_route("List", "Asset Movement"); + }, __("View")); } if(!this.frm.doc.is_return && this.frm.doc.status!="Closed") { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 46eee31e90..c7083be286 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -393,6 +393,8 @@ def make_purchase_invoice(source_name, target_doc=None): "parent": "purchase_receipt", "purchase_order_item": "po_detail", "purchase_order": "purchase_order", + "is_fixed_asset": "is_fixed_asset", + "asset": "asset", }, "postprocess": update_item, "filter": lambda d: abs(d.qty) - abs(invoiced_qty_map.get(d.name, 0))<=0 diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py index 9ade1afd8a..bcedd7179a 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py @@ -5,6 +5,7 @@ def get_data(): 'fieldname': 'purchase_receipt_no', 'non_standard_fieldnames': { 'Purchase Invoice': 'purchase_receipt', + 'Asset': 'purchase_receipt', 'Landed Cost Voucher': 'receipt_document', 'Subscription': 'reference_document' }, @@ -16,7 +17,7 @@ def get_data(): 'transactions': [ { 'label': _('Related'), - 'items': ['Purchase Invoice', 'Landed Cost Voucher'] + 'items': ['Purchase Invoice', 'Landed Cost Voucher', 'Asset'] }, { 'label': _('Reference'), diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index d9f40cca90..ffcc954df1 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -289,13 +289,53 @@ class TestPurchaseReceipt(unittest.TestCase): serial_no=serial_no, basic_rate=100, do_not_submit=True) self.assertRaises(SerialNoDuplicateError, se.submit) + def test_serialized_asset_item(self): + asset_item = "Test Serialized Asset Item" + + if not frappe.db.exists('Item', asset_item): + asset_category = frappe.get_all('Asset Category') + + if asset_category: + asset_category = asset_category[0].name + + if not asset_category: + doc = frappe.get_doc({ + 'doctype': 'Asset Category', + 'asset_category_name': 'Test Asset Category', + 'depreciation_method': 'Straight Line', + 'total_number_of_depreciations': 12, + 'frequency_of_depreciation': 1, + 'accounts': [{ + 'company_name': '_Test Company', + 'fixed_asset_account': '_Test Fixed Asset - _TC', + 'accumulated_depreciation_account': 'Depreciation - _TC', + 'depreciation_expense_account': 'Depreciation - _TC' + }] + }).insert() + + asset_category = doc.name + + asset_item = make_item(asset_item, {'is_stock_item':0, + 'stock_uom': 'Box', 'is_fixed_asset': 1, 'has_serial_no': 1, + 'asset_category': asset_category, 'serial_no_series': 'ABC.###'}) + + pr = make_purchase_receipt(item_code=asset_item, qty=3) + asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name}, 'name') + asset_movement = frappe.db.get_value('Asset Movement', {'reference_name': pr.name}, 'name') + serial_nos = frappe.get_all('Serial No', {'asset': asset}, 'name') + + self.assertEquals(len(serial_nos), 3) + pr.cancel() + serial_nos = frappe.get_all('Serial No', {'asset': asset}, 'name') or [] + self.assertEquals(len(serial_nos), 0) + frappe.db.sql("delete from `tabAsset Category`") + frappe.db.sql("delete from `tabAsset`") def get_gl_entries(voucher_type, voucher_no): return frappe.db.sql("""select account, debit, credit from `tabGL Entry` where voucher_type=%s and voucher_no=%s order by account desc""", (voucher_type, voucher_no), as_dict=1) - def make_purchase_receipt(**args): frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1) pr = frappe.new_doc("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 754bd71879..a7b0a03c0a 100755 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -1577,6 +1577,70 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "is_fixed_asset", + "fieldtype": "Check", + "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": "Is Fixed Asset", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "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, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "is_fixed_asset", + "fieldname": "asset", + "fieldtype": "Link", + "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": "Asset", + "length": 0, + "no_copy": 1, + "options": "Asset", + "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, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2395,7 +2459,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-02-22 15:15:38.793425", + "modified": "2018-04-23 14:07:48.438379", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json index b37713be96..fa4fa694c1 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.json +++ b/erpnext/stock/doctype/serial_no/serial_no.json @@ -41,6 +41,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -69,6 +70,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -100,6 +102,7 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -132,6 +135,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -165,6 +169,39 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "asset", + "fieldtype": "Link", + "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": "Asset", + "length": 0, + "no_copy": 1, + "options": "Asset", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "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, + "translatable": 0, "unique": 0 }, { @@ -193,6 +230,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -222,6 +260,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -253,6 +292,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "300px" }, @@ -287,6 +327,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -319,6 +360,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -348,6 +390,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -376,6 +419,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "50%" }, @@ -407,6 +451,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -437,6 +482,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -468,6 +514,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -497,6 +544,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -529,6 +577,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -557,6 +606,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "50%" }, @@ -588,6 +638,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -617,6 +668,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -647,6 +699,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -677,6 +730,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -707,6 +761,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -738,6 +793,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -767,6 +823,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -799,6 +856,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -827,6 +885,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "50%" }, @@ -860,6 +919,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -891,6 +951,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -921,6 +982,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -952,6 +1014,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -981,6 +1044,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1009,6 +1073,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "50%" }, @@ -1042,6 +1107,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" }, @@ -1074,6 +1140,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" }, @@ -1103,6 +1170,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "50%" }, @@ -1135,6 +1203,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" }, @@ -1167,6 +1236,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" }, @@ -1197,6 +1267,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1226,6 +1297,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1256,6 +1328,7 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -1270,7 +1343,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-05-15 18:22:23.685286", + "modified": "2018-04-19 20:25:52.066995", "modified_by": "Administrator", "module": "Stock", "name": "Serial No", @@ -1278,7 +1351,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -1298,7 +1370,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -1318,7 +1389,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 98f15a831a..17bf1bb1c3 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe +from frappe.model.naming import make_autoname from frappe.utils import cint, cstr, flt, add_days, nowdate, getdate from frappe import _, ValidationError @@ -276,24 +277,31 @@ def allow_serial_nos_with_different_item(sle_serial_no, sle): def update_serial_nos(sle, item_det): if sle.is_cancelled == "No" and not sle.serial_no and sle.actual_qty > 0 \ and item_det.has_serial_no == 1 and item_det.serial_no_series: - from frappe.model.naming import make_autoname - serial_nos = [] - for i in range(cint(sle.actual_qty)): - serial_nos.append(make_autoname(item_det.serial_no_series, "Serial No")) - frappe.db.set(sle, "serial_no", "\n".join(serial_nos)) + serial_nos = get_auto_serial_nos(item_det.serial_no_series, sle.actual_qty) + frappe.db.set(sle, "serial_no", serial_nos) validate_serial_no(sle, item_det) if sle.serial_no: - serial_nos = get_serial_nos(sle.serial_no) - for serial_no in serial_nos: - if frappe.db.exists("Serial No", serial_no): - sr = frappe.get_doc("Serial No", serial_no) - sr.via_stock_ledger = True - sr.item_code = sle.item_code - sr.warehouse = sle.warehouse if sle.actual_qty > 0 else None - sr.save(ignore_permissions=True) - elif sle.actual_qty > 0: - make_serial_no(serial_no, sle) + auto_make_serial_nos(sle) + +def get_auto_serial_nos(serial_no_series, qty): + serial_nos = [] + for i in range(cint(qty)): + serial_nos.append(make_autoname(serial_no_series, "Serial No")) + + return "\n".join(serial_nos) + +def auto_make_serial_nos(args): + serial_nos = get_serial_nos(args.get('serial_no')) + for serial_no in serial_nos: + if frappe.db.exists("Serial No", serial_no): + sr = frappe.get_doc("Serial No", serial_no) + sr.via_stock_ledger = True + sr.item_code = args.get('item_code') + sr.warehouse = args.get('warehouse') if args.get('actual_qty', 0) > 0 else None + sr.save(ignore_permissions=True) + elif args.get('actual_qty', 0) > 0: + make_serial_no(serial_no, args) def get_item_details(item_code): return frappe.db.sql("""select name, has_batch_no, docstatus, @@ -304,20 +312,26 @@ def get_serial_nos(serial_no): return [s.strip() for s in cstr(serial_no).strip().upper().replace(',', '\n').split('\n') if s.strip()] -def make_serial_no(serial_no, sle): +def make_serial_no(serial_no, args): sr = frappe.new_doc("Serial No") sr.warehouse = None sr.dont_update_if_missing.append("warehouse") sr.flags.ignore_permissions = True sr.serial_no = serial_no - sr.item_code = sle.item_code - sr.company = sle.company - sr.via_stock_ledger = True + sr.item_code = args.get('item_code') + sr.company = args.get('company') + sr.via_stock_ledger = args.get('via_stock_ledger') or True sr.insert() - sr.warehouse = sle.warehouse - sr.save() + if args.get('purchase_document_type'): + sr.purchase_document_type = args.get('purchase_document_type') + sr.purchase_document_no = args.get('purchase_document_no') + + if args.get('warehouse'): + sr.warehouse = args.get('warehouse') + sr.save() + frappe.msgprint(_("Serial No {0} created").format(sr.name)) return sr.name diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 7e456dd3d3..0d03b4d2ec 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -287,6 +287,10 @@ def get_default_income_account(args, item): or frappe.db.get_value("Item Group", item.item_group, "default_income_account")) def get_default_expense_account(args, item): + if item and item.is_fixed_asset: + return frappe.db.get_value("Company", args.company, "fixed_asset_account") + if account: return account + return (item.expense_account or args.expense_account or frappe.db.get_value("Item Group", item.item_group, "default_expense_account"))