diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index c1a2c9741b..9a6deada72 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -115,7 +115,6 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ } this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes"); - var me = this; if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) { frappe.model.with_doc("Supplier", me.frm.doc.supplier, function() { var supplier = frappe.model.get_doc("Supplier", me.frm.doc.supplier); diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 5d26d8a0e9..545967eb97 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -81,7 +81,6 @@ class PurchaseInvoice(BuyingController): self.validate_write_off_account() self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items") self.validate_fixed_asset() - self.validate_fixed_asset_account() self.create_remarks() self.set_status() validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference) @@ -176,7 +175,8 @@ class PurchaseInvoice(BuyingController): if self.update_stock: for d in self.get('items'): if not d.warehouse: - frappe.throw(_("Warehouse required at Row No {0}").format(d.idx)) + frappe.throw(_("Warehouse required at Row No {0}, please set default warehouse for the item {1} for the company {2}"). + format(d.idx, d.item_code, self.company)) super(PurchaseInvoice, self).validate_warehouse() @@ -390,10 +390,10 @@ class PurchaseInvoice(BuyingController): warehouse_account = get_warehouse_account_map() for item in self.get("items"): - if flt(item.base_net_amount) and item.item_code in stock_items: + if flt(item.base_net_amount): account_currency = get_account_currency(item.expense_account) - if self.update_stock and self.auto_accounting_for_stock: + if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items: val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9 # warehouse account @@ -435,7 +435,7 @@ class PurchaseInvoice(BuyingController): "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "credit": flt(item.rm_supp_cost) }, warehouse_account[self.supplier_warehouse]["account_currency"])) - else: + elif not item.is_fixed_asset: gl_entries.append( self.get_gl_dict({ "account": item.expense_account, @@ -478,11 +478,17 @@ class PurchaseInvoice(BuyingController): asset_amount = flt(item.net_amount) + flt(item.item_tax_amount/self.conversion_rate) base_asset_amount = flt(item.base_net_amount + item.item_tax_amount) + item.expense_account = item.expense_account or asset_accounts[0] + + if (not item.expense_account or frappe.db.get_value('Account', + item.expense_account, 'account_type') != 'Asset Received But Not Billed'): + frappe.throw(_("Row {0}: Expense account must be of type Asset Received But Not Billed"). + format(item.idx)) if not self.update_stock: - asset_rbnb_currency = get_account_currency(asset_accounts[0]) + asset_rbnb_currency = get_account_currency(item.expense_account) gl_entries.append(self.get_gl_dict({ - "account": asset_accounts[0], + "account": item.expense_account, "against": self.supplier, "remarks": self.get("remarks") or _("Accounting Entry for Asset"), "debit": base_asset_amount, @@ -517,7 +523,7 @@ class PurchaseInvoice(BuyingController): })) if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)): - asset_eiiav_currency = get_account_currency(asset_accounts[0]) + asset_eiiav_currency = get_account_currency(asset_accounts[1]) gl_entries.append(self.get_gl_dict({ "account": asset_accounts[1], "against": self.supplier, @@ -750,13 +756,6 @@ class PurchaseInvoice(BuyingController): for pr in set(updated_pr): frappe.get_doc("Purchase Receipt", pr).update_billing_percentage(update_modified=update_modified) - def validate_fixed_asset_account(self): - for d in self.get('items'): - if d.is_fixed_asset: - account_type = frappe.db.get_value("Account", d.expense_account, "account_type") - if account_type != 'Fixed Asset': - frappe.throw(_("Row {0}# Account must be of type 'Fixed Asset'").format(d.idx)) - def on_recurring(self, reference_doc, auto_repeat_doc): self.due_date = None diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js old mode 100644 new mode 100755 index c07b851864..2146aced2a --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -1051,6 +1051,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ item_name: obj.name === obj.item_name ? "" : obj.item_name, item_image: obj.image, item_stock: __('Stock Qty') + ": " + me.get_actual_qty(obj), + item_uom: obj.stock_uom, color: frappe.get_palette(obj.item_name), abbr: frappe.get_abbr(obj.item_name) })).tooltip().appendTo($wrap); diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 69b369f081..2222f1f431 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -487,7 +487,7 @@ def get_patry_tax_withholding_details(ref_doc): if tax.valid_till and date_diff(tax.valid_till, ref_doc.posting_date) > 0: tax_mapper.update({ - "rate": tax.applicable_percentage + "rate": tax.applicable_percent }) prepare_tax_withholding_details(tax_mapper, tax_withholding_details) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 0b1fc6da84..750120b997 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -15,6 +15,10 @@ from erpnext.accounts.report.cash_flow.cash_flow import (get_cash_flow_accounts, def execute(filters=None): columns, data, message, chart = [], [], [], [] + + if not filters.get('company'): + return columns, data, message, chart + fiscal_year = get_fiscal_year_data(filters.get('from_fiscal_year'), filters.get('to_fiscal_year')) companies_column, companies = get_companies(filters) columns = get_columns(companies_column) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 2d0bd52fd0..7134b91cd0 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -11,6 +11,9 @@ from erpnext.accounts.utils import get_account_currency def execute(filters=None): + if not filters: + return [], [] + account_details = {} if filters and filters.get('print_in_account_currency') and \ diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index c05667a767..658107bb18 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -82,7 +82,7 @@ frappe.ui.form.on('Asset', { } if (frm.doc.status != 'Fully Depreciated') { frm.add_custom_button(__("Asset Adjustment"), function() { - frm.trigger("create_asset_maintenance"); + frm.trigger("create_asset_adjustment"); }, __("Make")); } @@ -157,6 +157,13 @@ frappe.ui.form.on('Asset', { } }, + available_for_use_date: function(frm) { + $.each(frm.doc.finance_books || [], function(i, d) { + if(!d.depreciation_start_date) d.depreciation_start_date = frm.doc.available_for_use_date; + }); + refresh_field("finance_books"); + }, + is_existing_asset: function(frm) { // frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation)); }, @@ -226,6 +233,22 @@ frappe.ui.form.on('Asset', { }) }, + create_asset_adjustment: function(frm) { + frappe.call({ + args: { + "asset": frm.doc.name, + "asset_category": frm.doc.asset_category, + "company": frm.doc.company + }, + method: "erpnext.assets.doctype.asset.asset.create_asset_adjustment", + freeze: 1, + callback: function(r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }) + }, + calculate_depreciation: function(frm) { frappe.db.get_value("Asset Settings", {'name':"Asset Settings"}, 'schedule_based_on_fiscal_year', (data) => { if (data.schedule_based_on_fiscal_year == 1) { @@ -321,6 +344,42 @@ erpnext.asset.transfer_asset = function(frm) { }, "reqd": 1 }, + { + "label": __("Select Serial No"), + "fieldname": "serial_nos", + "fieldtype": "Link", + "options": "Serial No", + "get_query": function () { + return { + filters: { + 'asset': frm.doc.name + } + } + }, + "onchange": function() { + let val = this.get_value(); + if (val) { + let serial_nos = dialog.get_value("serial_no") || val; + if (serial_nos) { + serial_nos = serial_nos.split('\n'); + serial_nos.push(val); + + const unique_sn = serial_nos.filter(function(elem, index, self) { + return index === self.indexOf(elem); + }); + + dialog.set_value("serial_no", unique_sn.join('\n')); + dialog.set_value("serial_nos", ""); + } + } + } + }, + { + "label": __("Serial No"), + "fieldname": "serial_no", + "read_only": 1, + "fieldtype": "Small Text" + }, { "label": __("Date"), "fieldname": "transfer_date", @@ -342,8 +401,9 @@ erpnext.asset.transfer_asset = function(frm) { args: { "asset": frm.doc.name, "transaction_date": args.transfer_date, - "source_warehouse": frm.doc.location, - "target_warehouse": args.target_location, + "source_location": frm.doc.location, + "target_location": args.target_location, + "serial_no": args.serial_no, "company": frm.doc.company } }, diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index e37038a5a8..1069db636a 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -51,7 +51,7 @@ class Asset(AccountsController): def validate_in_use_date(self): if not self.available_for_use_date: - frappe.throw(_("Available for use data is required")) + frappe.throw(_("Available for use date is required")) def set_missing_values(self): if not self.asset_category: @@ -162,6 +162,9 @@ class Asset(AccountsController): frappe.throw(_("Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount") .format(row.idx)) + if not row.depreciation_start_date: + frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx)) + if not self.is_existing_asset: self.opening_accumulated_depreciation = 0 self.number_of_depreciations_booked = 0 @@ -418,10 +421,24 @@ def create_asset_maintenance(asset, item_code, item_name, asset_category, compan }) return asset_maintenance +@frappe.whitelist() +def create_asset_adjustment(asset, asset_category, company): + asset_maintenance = frappe.new_doc("Asset Adjustment") + asset_maintenance.update({ + "asset": asset, + "company": company, + "asset_category": asset_category + }) + return asset_maintenance + @frappe.whitelist() def transfer_asset(args): import json args = json.loads(args) + + if args.get('serial_no'): + args['quantity'] = len(args.get('serial_no').split('\n')) + movement_entry = frappe.new_doc("Asset Movement") movement_entry.update(args) movement_entry.insert() diff --git a/erpnext/assets/doctype/asset_adjustment/asset_adjustment.js b/erpnext/assets/doctype/asset_adjustment/asset_adjustment.js index 11c02e105f..346a2f1d71 100644 --- a/erpnext/assets/doctype/asset_adjustment/asset_adjustment.js +++ b/erpnext/assets/doctype/asset_adjustment/asset_adjustment.js @@ -2,6 +2,18 @@ // For license information, please see license.txt frappe.ui.form.on('Asset Adjustment', { + setup: function(frm) { + frm.add_fetch('company', 'cost_center', 'cost_center'); + frm.set_query('cost_center', function() { + return { + filters: { + company: frm.doc.company, + is_group: 0 + } + } + }); + }, + asset: function(frm) { frm.trigger("set_current_asset_value"); }, @@ -11,7 +23,6 @@ frappe.ui.form.on('Asset Adjustment', { }, set_current_asset_value: function(frm) { - debugger if (frm.doc.finance_book && frm.doc.asset) { frm.call({ method: "erpnext.assets.doctype.asset_adjustment.asset_adjustment.get_current_asset_value", diff --git a/erpnext/assets/doctype/asset_adjustment/asset_adjustment.json b/erpnext/assets/doctype/asset_adjustment/asset_adjustment.json index faa36efe07..3f4f77c328 100644 --- a/erpnext/assets/doctype/asset_adjustment/asset_adjustment.json +++ b/erpnext/assets/doctype/asset_adjustment/asset_adjustment.json @@ -41,6 +41,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -72,6 +73,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -103,6 +105,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -134,6 +137,7 @@ "reqd": 1, "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 }, { @@ -194,6 +199,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -203,7 +209,7 @@ "collapsible": 0, "columns": 0, "fieldname": "date", - "fieldtype": "Datetime", + "fieldtype": "Date", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -224,6 +230,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -254,6 +261,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -284,6 +292,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -314,6 +323,39 @@ "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": "cost_center", + "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": "Cost Center", + "length": 0, + "no_copy": 0, + "options": "Cost Center", + "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 }, { @@ -344,6 +386,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -357,7 +400,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-11 21:45:03.459696", + "modified": "2018-05-17 11:12:38.110774", "modified_by": "Administrator", "module": "Assets", "name": "Asset Adjustment", @@ -366,7 +409,6 @@ "permissions": [ { "amend": 1, - "apply_user_permissions": 0, "cancel": 1, "create": 1, "delete": 1, @@ -386,7 +428,6 @@ }, { "amend": 1, - "apply_user_permissions": 0, "cancel": 1, "create": 1, "delete": 1, @@ -406,7 +447,6 @@ }, { "amend": 1, - "apply_user_permissions": 0, "cancel": 1, "create": 1, "delete": 1, diff --git a/erpnext/assets/doctype/asset_adjustment/asset_adjustment.py b/erpnext/assets/doctype/asset_adjustment/asset_adjustment.py index 6b4b752e7f..af806e4a8d 100644 --- a/erpnext/assets/doctype/asset_adjustment/asset_adjustment.py +++ b/erpnext/assets/doctype/asset_adjustment/asset_adjustment.py @@ -16,12 +16,14 @@ class AssetAdjustment(Document): def on_submit(self): self.make_depreciation_entry() - self.reschedule_depreciations() + self.reschedule_depreciations(self.new_asset_value) def on_cancel(self): if self.journal_entry: frappe.throw(_("Cancel the journal entry {0} first").format(self.journal_entry)) + self.reschedule_depreciations(self.current_asset_value) + def set_difference_amount(self): self.difference_amount = flt(self.current_asset_value - self.new_asset_value) @@ -47,12 +49,13 @@ class AssetAdjustment(Document): je.append("accounts", { "account": accumulated_depreciation_account, "credit_in_account_currency": self.difference_amount, + "cost_center": depreciation_cost_center or self.cost_center }) je.append("accounts", { "account": depreciation_expense_account, "debit_in_account_currency": self.difference_amount, - "cost_center": depreciation_cost_center + "cost_center": depreciation_cost_center or self.cost_center }) je.flags.ignore_permissions = True @@ -60,11 +63,11 @@ class AssetAdjustment(Document): self.db_set("journal_entry", je.name) - def reschedule_depreciations(self): + def reschedule_depreciations(self, asset_value): asset = frappe.get_doc('Asset', self.asset) for d in asset.finance_books: - d.value_after_depreciation = self.new_asset_value + d.value_after_depreciation = asset_value if d.depreciation_method in ("Straight Line", "Manual"): end_date = max([s.schedule_date for s in asset.schedules if cint(s.finance_book_id) == d.idx]) diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py index e34d2a3066..638987ee96 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/asset_movement.py @@ -55,7 +55,5 @@ class AssetMovement(Document): frappe.db.set_value("Asset", self.asset, "location", location) if self.serial_no: - serial_nos = get_serial_nos(self.serial_no) - - frappe.db.sql(""" update `tabSerial No` set location = %s where name in (%s)""" - %('%s', ','.join(['%s'] * len(serial_nos))), (location, tuple(serial_nos))) + for d in get_serial_nos(self.serial_no): + frappe.db.set_value('Serial No', d, 'location', location) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a16047c34e..adab972ecd 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -621,8 +621,8 @@ class AccountsController(TransactionBase): def validate_fixed_asset(self): for d in self.get("items"): if d.is_fixed_asset: - 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.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") and d.asset: asset = frappe.get_doc("Asset", d.asset) @@ -635,8 +635,8 @@ class AccountsController(TransactionBase): 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": diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 8b6dd176e1..a4e95a1bc1 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -366,7 +366,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select tabAccount.name from `tabAccount` where (tabAccount.report_type = "Profit and Loss" - or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary")) + or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed")) and tabAccount.is_group=0 and tabAccount.docstatus!=2 and tabAccount.{key} LIKE %(txt)s diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json index 9dee848029..c5691f72ee 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json @@ -139,6 +139,37 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "total_exemption_amount", + "fieldtype": "Currency", + "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": "Total Exemption Amount", + "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 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -243,7 +274,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-15 16:16:46.075493", + "modified": "2018-05-16 19:03:57.624215", "modified_by": "Administrator", "module": "HR", "name": "Employee Tax Exemption Declaration", diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py index 52746d4cff..22e1638751 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py @@ -19,3 +19,6 @@ class EmployeeTaxExemptionDeclaration(Document): "docstatus": 1}): frappe.throw(_("Tax Declaration of {0} for period {1} already submitted.")\ .format(self.employee, self.payroll_period), frappe.DocstatusTransitionError) + self.total_exemption_amount = 0 + for item in self.declarations: + self.total_exemption_amount += item.amount diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py index b58d334d29..6cdd22fc21 100644 --- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py +++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py @@ -14,7 +14,7 @@ class EmployeeTransfer(Document): if frappe.get_value("Employee", self.employee, "status") == "Left": frappe.throw(_("Cannot transfer Employee with status Left")) if self.new_company and self.company == self.new_company: - frappe.throw_("New Company must be different from current company") + frappe.throw(_("New Company must be different from current company")) def before_submit(self): if getdate(self.transfer_date) > getdate(): diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 5f1c8830fb..7a6b24658b 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -50,8 +50,11 @@ frappe.ui.form.on("Leave Application", { date: frm.doc.posting_date }, callback: function(r) { - if (!r.exc && r.message) { - leave_details = r.message; + if (!r.exc && r.message['leave_allocation']) { + leave_details = r.message['leave_allocation']; + } + if (!r.exc && r.message['leave_approver']) { + frm.set_value('leave_approver', r.message['leave_approver']); } } }); @@ -74,6 +77,13 @@ frappe.ui.form.on("Leave Application", { if(frm.doc.__islocal && !in_list(frappe.user_roles, "Employee")) { frm.set_intro(__("Fill the form and save it")); } + + if (!frm.doc.employee && frappe.defaults.get_user_permissions()) { + const perm = frappe.defaults.get_user_permissions(); + if (perm && perm['Employee']) { + frm.set_value('employee', perm['Employee']["docs"][0]) + } + } }, employee: function(frm) { diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index d0305c3902..c58e0cf5cc 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -374,7 +374,12 @@ def get_leave_details(employee, date): "pending_leaves": leaves_pending, "remaining_leaves": remaining_leaves} - return leave_allocation + ret = { + 'leave_allocation': leave_allocation, + 'leave_approver': get_leave_approver(employee) + } + + return ret @frappe.whitelist() def get_leave_balance_on(employee, leave_type, date, allocation_records=None, @@ -603,3 +608,10 @@ def get_approved_leaves_for_period(employee, leave_type, from_date, to_date): return leave_days +def get_leave_approver(employee, department=None): + if not department: + department = frappe.db.get_value('Employee', employee, 'department') + + if department: + return frappe.db.get_value('Department Approver', {'parent': department, + 'parentfield': 'leave_approver', 'idx': 1}, 'approver') diff --git a/erpnext/hr/doctype/salary_component/salary_component.py b/erpnext/hr/doctype/salary_component/salary_component.py index 9a43069619..132930f563 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.py +++ b/erpnext/hr/doctype/salary_component/salary_component.py @@ -34,3 +34,12 @@ class SalaryComponent(Document): self.salary_component_abbr = self.salary_component_abbr.strip() self.salary_component_abbr = append_number_if_name_exists('Salary Component', self.salary_component_abbr, 'salary_component_abbr', separator='_', filters={"name": ["!=", self.name]}) + + def calculate_tax(self, annual_earning): + taxable_amount = 0 + for slab in self.taxable_salary_slabs: + if annual_earning > slab.from_amount and annual_earning < slab.to_amount: + taxable_amount += (annual_earning - slab.from_amount) * slab.percent_deduction *.01 + elif annual_earning > slab.from_amount and annual_earning > slab.to_amount: + taxable_amount += (slab.to_amount - slab.from_amount) * slab.percent_deduction * .01 + return taxable_amount diff --git a/erpnext/hr/doctype/salary_detail/salary_detail.json b/erpnext/hr/doctype/salary_detail/salary_detail.json index 01d02779b7..862728afb7 100644 --- a/erpnext/hr/doctype/salary_detail/salary_detail.json +++ b/erpnext/hr/doctype/salary_detail/salary_detail.json @@ -40,6 +40,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -72,6 +73,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -101,6 +103,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -132,6 +135,100 @@ "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": "is_tax_applicable", + "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": "Is Tax Applicable", + "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": "is_flexible_benefit", + "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": "Is Flexible Benefit", + "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": "variable_based_on_taxable_salary", + "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": "Variable Based On Taxable Salary", + "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 }, { @@ -161,6 +258,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -192,6 +290,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -225,6 +324,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -258,6 +358,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -290,6 +391,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -321,6 +423,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -351,6 +454,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -383,6 +487,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -413,6 +518,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -445,6 +551,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -458,7 +565,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-10-02 13:57:22.769751", + "modified": "2018-05-16 22:42:59.974450", "modified_by": "Administrator", "module": "HR", "name": "Salary Detail", diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 5f284789fa..87511b6e9d 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -16,6 +16,7 @@ from erpnext.hr.doctype.additional_salary_component.additional_salary_component from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_employee_benefit_application, get_amount from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days from erpnext.hr.doctype.employee_benefit_claim.employee_benefit_claim import get_employee_benefit_claim +from erpnext.hr.utils import get_payroll_period class SalarySlip(TransactionBase): def autoname(self): @@ -61,6 +62,10 @@ class SalarySlip(TransactionBase): amount = self.eval_condition_and_formula(struct_row, data) if amount and struct_row.statistical_component == 0: self.update_component_row(struct_row, amount, key) + if key=="deductions" and struct_row.variable_based_on_taxable_salary: + tax_row, amount = self.calculate_pro_rata_tax(struct_row.salary_component) + if tax_row and amount: + self.update_component_row(frappe._dict(tax_row), amount, key) additional_components = get_additional_salary_component(self.employee, self.start_date, self.end_date) if additional_components: @@ -518,6 +523,52 @@ class SalarySlip(TransactionBase): status = "Cancelled" return status + def calculate_pro_rata_tax(self, salary_component): + # Calculate total tax payable earnings + tax_applicable_components = [] + for earning in self._salary_structure_doc.earnings: + #all tax applicable earnings which are not flexi + if earning.is_tax_applicable and not earning.is_flexible_benefit: + tax_applicable_components.append(earning.salary_component) + total_taxable_earning = 0 + for earning in self.earnings: + if earning.salary_component in tax_applicable_components: + total_taxable_earning += earning.amount + + # Get payroll period, prorata frequency + days = date_diff(self.end_date, self.start_date) + 1 + payroll_period = get_payroll_period(self.start_date, self.end_date, self.company) + if not payroll_period: + frappe.throw(_("Start and end dates not in a valid Payroll Period")) + total_days = date_diff(payroll_period.end_date, payroll_period.start_date) + 1 + prorata_frequency = flt(total_days)/flt(days) + annual_earning = total_taxable_earning * prorata_frequency + + # Calculate total exemption declaration + exemption_amount = 0 + if frappe.db.exists("Employee Tax Exemption Declaration", {"employee": self.employee, + "payroll_period": payroll_period.name, "docstatus": 1}): + exemption_amount = frappe.db.get_value("Employee Tax Exemption Declaration", + {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, #fix period + "total_exemption_amount") + annual_earning = annual_earning - exemption_amount + + # Get tax calc by component + component = frappe.get_doc("Salary Component", salary_component) + annual_tax = component.calculate_tax(annual_earning) + + # Calc prorata tax + pro_rata_tax = annual_tax/prorata_frequency + + # Data for update_component_row + struct_row = {} + struct_row['depends_on_lwp'] = 0 + struct_row['salary_component'] = component.name + struct_row['abbr'] = component.salary_component_abbr + struct_row['do_not_include_in_total'] = 0 + + return struct_row, pro_rata_tax + def unlink_ref_doc_from_salary_slip(ref_no): linked_ss = frappe.db.sql_list("""select name from `tabSalary Slip` where journal_entry=%s and docstatus < 2""", (ref_no)) diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js index c63887bcf6..9e62b6640e 100755 --- a/erpnext/hr/doctype/salary_structure/salary_structure.js +++ b/erpnext/hr/doctype/salary_structure/salary_structure.js @@ -192,17 +192,20 @@ frappe.ui.form.on('Salary Detail', { callback: function(data) { if(data.message){ var result = data.message; - frappe.model.set_value(cdt, cdn, 'condition',result.condition); - frappe.model.set_value(cdt, cdn, 'amount_based_on_formula',result.amount_based_on_formula); + frappe.model.set_value(cdt, cdn, 'condition', result.condition); + frappe.model.set_value(cdt, cdn, 'amount_based_on_formula', result.amount_based_on_formula); if(result.amount_based_on_formula == 1){ - frappe.model.set_value(cdt, cdn, 'formula',result.formula); + frappe.model.set_value(cdt, cdn, 'formula', result.formula); } else{ - frappe.model.set_value(cdt, cdn, 'amount',result.amount); + frappe.model.set_value(cdt, cdn, 'amount', result.amount); } - frappe.model.set_value(cdt, cdn, 'statistical_component',result.statistical_component); - frappe.model.set_value(cdt, cdn, 'depends_on_lwp',result.depends_on_lwp); - frappe.model.set_value(cdt, cdn, 'do_not_include_in_total',result.do_not_include_in_total); + frappe.model.set_value(cdt, cdn, 'statistical_component', result.statistical_component); + frappe.model.set_value(cdt, cdn, 'depends_on_lwp', result.depends_on_lwp); + frappe.model.set_value(cdt, cdn, 'do_not_include_in_total', result.do_not_include_in_total); + frappe.model.set_value(cdt, cdn, 'variable_based_on_taxable_salary', result.variable_based_on_taxable_salary); + frappe.model.set_value(cdt, cdn, 'is_tax_applicable', result.is_tax_applicable); + frappe.model.set_value(cdt, cdn, 'is_flexible_benefit', result.is_flexible_benefit); refresh_field("earnings"); refresh_field("deductions"); } diff --git a/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.json b/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.json index bd0ec6be31..8e6c1d86ae 100644 --- a/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.json +++ b/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.json @@ -50,7 +50,7 @@ "collapsible": 0, "columns": 0, "fieldname": "to_amount", - "fieldtype": "Data", + "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -147,7 +147,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-04-13 20:09:36.675987", + "modified": "2018-05-16 18:18:23.802576", "modified_by": "Administrator", "module": "HR", "name": "Taxable Salary Slab", diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index df720c4cc6..20fe666d2b 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -234,3 +234,10 @@ def get_leave_period(from_date, to_date, company): if leave_period: return leave_period + +def get_payroll_period(from_date, to_date, company): + payroll_period = frappe.db.sql("""select pp.name, pd.start_date, pd.end_date from + `tabPayroll Period Date` pd join `tabPayroll Period` pp on + pd.parent=pp.name where pd.start_date<=%s and pd.end_date>= %s + and pp.company=%s""", (from_date, to_date, company), as_dict=1) + return payroll_period[0] if payroll_period else None diff --git a/erpnext/public/js/pos/pos_item.html b/erpnext/public/js/pos/pos_item.html old mode 100644 new mode 100755 index f6ef5a42d2..52f3cf698a --- a/erpnext/public/js/pos/pos_item.html +++ b/erpnext/public/js/pos/pos_item.html @@ -25,7 +25,7 @@ {% } %} - {{item_price}} + {{item_price}} / {{item_uom}}