diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js
new file mode 100644
index 0000000000..0627675de7
--- /dev/null
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Accounts Settings', {
+ refresh: function(frm) {
+
+ }
+});
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index 1ba9221b38..d46c2d5606 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -2,11 +2,13 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-06-24 15:49:57",
"custom": 0,
"description": "Settings for Accounts",
"docstatus": 0,
"doctype": "DocType",
+ "document_type": "Other",
"fields": [
{
"allow_on_submit": 0,
@@ -18,6 +20,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Make Accounting Entry For Every Stock Movement",
@@ -42,6 +45,7 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Accounts Frozen Upto",
@@ -66,6 +70,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries",
@@ -91,6 +96,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Credit Controller",
@@ -115,6 +121,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Check Supplier Invoice Number Uniqueness",
@@ -136,13 +143,14 @@
"hide_toolbar": 0,
"icon": "icon-cog",
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
- "modified": "2015-12-24 21:42:01.274459",
+ "modified": "2016-06-27 15:18:27.566087",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
@@ -169,6 +177,9 @@
"write": 1
}
],
+ "quick_entry": 1,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "sort_order": "ASC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index be8a846bd8..d64a024222 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -429,4 +429,30 @@ cur_frm.set_query("asset", "items", function(doc, cdt, cdn) {
["Asset", "company", "=", doc.company]
]
}
-});
\ No newline at end of file
+});
+
+frappe.ui.form.on('Sales Invoice', {
+ setup: function(frm){
+ frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(doc, cdt, cdn){
+ return {
+ filters: [
+ ["Time Sheet", "status", "in", ["Submitted", "Payslip"]]
+ ]
+ }
+ }
+ }
+})
+
+frappe.ui.form.on('Sales Invoice Timesheet', {
+ time_sheet: function(frm){
+ frm.call({
+ method: "calculate_billing_amount_from_timesheet",
+ doc: frm.doc,
+ callback: function(r, rt) {
+ refresh_field('total_billing_amount')
+ }
+ })
+ }
+})
+
+cur_frm.add_fetch("time_sheet", "total_billing_amount", "billing_amount");
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 357dec2b93..c68c262657 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -1065,6 +1065,85 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": "",
+ "depends_on": "eval:doc.total_billing_amount > 0",
+ "fieldname": "time_sheet_list",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Time Sheet List",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "timesheets",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Time Sheets",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Sales Invoice Timesheet",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "default": "0",
+ "fieldname": "total_billing_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Total Billing Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "2",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
{
"allow_on_submit": 0,
"bold": 0,
@@ -3597,7 +3676,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-06-10 12:57:08.818701",
+ "modified": "2016-06-27 15:13:36.523798",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 2584d407d5..8f7c3411e5 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -79,9 +79,10 @@ class SalesInvoice(SellingController):
self.set_against_income_account()
self.validate_c_form()
- self.validate_time_logs_are_submitted()
+ self.validate_time_sheets_are_submitted()
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items")
self.update_packing_list()
+ self.calculate_billing_amount_from_timesheet()
def before_save(self):
set_account_for_mode_of_payment(self)
@@ -125,11 +126,10 @@ class SalesInvoice(SellingController):
if not cint(self.is_pos) == 1 and not self.is_return:
self.update_against_document_in_jv()
- self.update_time_log_batch(self.name)
-
+ self.update_time_sheet(self.name)
def before_cancel(self):
- self.update_time_log_batch(None)
+ self.update_time_sheet(None)
def on_cancel(self):
self.check_close_sales_order("sales_order")
@@ -217,20 +217,21 @@ class SalesInvoice(SellingController):
if pos:
return {"print_format": pos.get("print_format") }
- def update_time_log_batch(self, sales_invoice):
- for d in self.get("items"):
- if d.time_log_batch:
- tlb = frappe.get_doc("Time Log Batch", d.time_log_batch)
- tlb.sales_invoice = sales_invoice
- tlb.flags.ignore_validate_update_after_submit = True
- tlb.save()
+ def update_time_sheet(self, sales_invoice):
+ for d in self.get("timesheets"):
+ if d.time_sheet:
+ timesheet = frappe.get_doc("Time Sheet", d.time_sheet)
+ timesheet.sales_invoice = sales_invoice
+ timesheet.flags.ignore_validate_update_after_submit = True
+ timesheet.set_status()
+ timesheet.save()
- def validate_time_logs_are_submitted(self):
- for d in self.get("items"):
- if d.time_log_batch:
- docstatus = frappe.db.get_value("Time Log Batch", d.time_log_batch, "docstatus")
- if docstatus!=1:
- frappe.throw(_("Time Log Batch {0} must be 'Submitted'").format(d.time_log_batch))
+ def validate_time_sheets_are_submitted(self):
+ for data in self.get("timesheets"):
+ if data.time_sheet:
+ status = frappe.db.get_value("Time Sheet", data.time_sheet, "status")
+ if status not in ['Submitted', 'Payslip']:
+ frappe.throw(_("Time Sheet {0} is already completed or cancelled").format(data.time_sheet))
def set_pos_fields(self, for_validate=False):
"""Set retail related fields from POS Profiles"""
@@ -450,6 +451,13 @@ class SalesInvoice(SellingController):
else:
self.set('packed_items', [])
+ def calculate_billing_amount_from_timesheet(self):
+ total_billing_amount = 0.0
+ for data in self.timesheets:
+ if data.billing_amount:
+ total_billing_amount += data.billing_amount
+
+ self.total_billing_amount = total_billing_amount
def get_warehouse(self):
user_pos_profile = frappe.db.sql("""select name, warehouse from `tabPOS Profile`
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index 021b18b024..2b3232c1da 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -3,6 +3,7 @@
"allow_import": 0,
"allow_rename": 0,
"autoname": "hash",
+ "beta": 0,
"creation": "2013-06-04 11:02:19",
"custom": 0,
"docstatus": 0,
@@ -1417,31 +1418,6 @@
"set_only_once": 0,
"unique": 0
},
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "time_log_batch",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Time Log Batch",
- "length": 0,
- "no_copy": 0,
- "options": "Time Log Batch",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
{
"allow_on_submit": 0,
"bold": 0,
@@ -1676,18 +1652,20 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2016-04-06 12:26:19.052860",
+ "modified": "2016-06-24 16:01:59.719026",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
diff --git a/erpnext/projects/doctype/time_log/__init__.py b/erpnext/accounts/doctype/sales_invoice_timesheet/__init__.py
similarity index 100%
rename from erpnext/projects/doctype/time_log/__init__.py
rename to erpnext/accounts/doctype/sales_invoice_timesheet/__init__.py
diff --git a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
new file mode 100644
index 0000000000..45ae82c11a
--- /dev/null
+++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
@@ -0,0 +1,87 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2016-06-14 19:21:34.321662",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "time_sheet",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Time Sheet",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Time Sheet",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "billing_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Billing Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "2",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2016-06-15 23:56:06.131202",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Invoice Timesheet",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.py b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.py
new file mode 100644
index 0000000000..afc05ab7ed
--- /dev/null
+++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class SalesInvoiceTimesheet(Document):
+ pass
diff --git a/erpnext/change_log/v5/v5_6_0.md b/erpnext/change_log/v5/v5_6_0.md
index 0ef5a3a2d0..4fb4115f15 100644
--- a/erpnext/change_log/v5/v5_6_0.md
+++ b/erpnext/change_log/v5/v5_6_0.md
@@ -3,4 +3,4 @@
Now additional costs like shipping charges, operating costs etc can be added in Stock Entry in item valuation
- **Update Finished Goods** in Production Order can now use the items from **Transfer Materials for Manufacture** step instead of items from the Bill of Materials. This can be configured in Manufacturing Settings
- Added field **Tax ID** in Customer
-- Bug fixes in Item, Time Log Batch, Pricing Rule, Salary Slip, Address and Stock Entry
+- Bug fixes in Item, Time Sheet, Pricing Rule, Salary Slip, Address and Stock Entry
diff --git a/erpnext/config/manufacturing.py b/erpnext/config/manufacturing.py
index 2ae49fe725..fbc9fb1701 100644
--- a/erpnext/config/manufacturing.py
+++ b/erpnext/config/manufacturing.py
@@ -23,8 +23,8 @@ def get_data():
},
{
"type": "doctype",
- "name": "Time Log",
- "description": _("Time Logs for manufacturing."),
+ "name": "Time Sheet",
+ "description": _("Time Sheet for manufacturing."),
},
]
diff --git a/erpnext/config/projects.py b/erpnext/config/projects.py
index 3deaeba91e..c2542fa738 100644
--- a/erpnext/config/projects.py
+++ b/erpnext/config/projects.py
@@ -31,13 +31,8 @@ def get_data():
"items": [
{
"type": "doctype",
- "name": "Time Log",
- "description": _("Time Log for tasks."),
- },
- {
- "type": "doctype",
- "name": "Time Log Batch",
- "description": _("Batch Time Logs for billing."),
+ "name": "Time Sheet",
+ "description": _("Time Sheet for tasks."),
},
{
"type": "doctype",
@@ -58,8 +53,8 @@ def get_data():
{
"type": "report",
"is_query_report": True,
- "name": "Daily Time Log Summary",
- "doctype": "Time Log"
+ "name": "Daily Time Sheet Summary",
+ "doctype": "Time Sheet"
},
{
"type": "report",
diff --git a/erpnext/docs/assets/img/project/time_log_batch.gif b/erpnext/docs/assets/img/project/time_sheet.gif
similarity index 100%
rename from erpnext/docs/assets/img/project/time_log_batch.gif
rename to erpnext/docs/assets/img/project/time_sheet.gif
diff --git a/erpnext/docs/assets/img/project/time_log_batch_make_invoice.png b/erpnext/docs/assets/img/project/time_sheet_make_invoice.png
similarity index 100%
rename from erpnext/docs/assets/img/project/time_log_batch_make_invoice.png
rename to erpnext/docs/assets/img/project/time_sheet_make_invoice.png
diff --git a/erpnext/docs/assets/img/project/time_log_batch_sales_invoice.png b/erpnext/docs/assets/img/project/time_sheet_sales_invoice.png
similarity index 100%
rename from erpnext/docs/assets/img/project/time_log_batch_sales_invoice.png
rename to erpnext/docs/assets/img/project/time_sheet_sales_invoice.png
diff --git a/erpnext/docs/user/manual/de/projects/time-log-batch.md b/erpnext/docs/user/manual/de/projects/time-log-batch.md
index bfc6fa81ef..6aa85cbe73 100644
--- a/erpnext/docs/user/manual/de/projects/time-log-batch.md
+++ b/erpnext/docs/user/manual/de/projects/time-log-batch.md
@@ -9,17 +9,17 @@ ODER
Öffnen Sie einfach Ihre Übersicht der Zeitprotokolle und kreuzen Sie die Artikel an, die Sie dem Zeitprotokollstapel hinzufügen möchten. Klicken Sie dann auf die Schaltfläche "Zeitprotokollstapel erstellen" und diese Zeitprotokolle werden ausgewählt.
-
+
### Ausgangsrechnungen erstellen
* Sobald Sie einen Zeitprotokollstapel übertragen haben, sollte die Schaltfläche "Rechnung erstellen" erscheinen.
-
+
* Klicken Sie auf diese Schaltfläche und erstellen Sie eine Ausgangsrechnung zu einem Zeitprotokollstapel.
-
+
* Wenn Sie die Ausgangsrechnung "übertragen", wird die Nummer der Ausgangsrechnung in den Zeitprotokollen und im Zeitprotokollstapel aktualisiert und ihr Status wird auf "abgerechnet" geändert.
diff --git a/erpnext/docs/user/manual/en/projects/time-log-batch.md b/erpnext/docs/user/manual/en/projects/time-log-batch.md
index d48633ad07..f4f76dc16b 100644
--- a/erpnext/docs/user/manual/en/projects/time-log-batch.md
+++ b/erpnext/docs/user/manual/en/projects/time-log-batch.md
@@ -1,23 +1,23 @@
-You can bill Time Logs by batching them together. This gives you the flexiblity to manage your customer billing in the way you want. To create a new Time Log Batch, go to
+You can bill Time Logs by batching them together. This gives you the flexiblity to manage your customer billing in the way you want. To create a new Time Sheet, go to
-> Projects > Time Log Batch > New Time Log Batch
+> Projects > Time Sheet > New Time Sheet
OR
-Just open your Time Log list and check the Items to you want to add to the Time Log. Then click on "Make Time Log Batch" button and these Time Logs will be selected.
+Just open your Time Log list and check the Items to you want to add to the Time Log. Then click on "Make Time Sheet" button and these Time Logs will be selected.
-
+
###Making Sales Invoice
-* After submitting the Time Log Batch, "Make Invoice" button shall appear.
+* After submitting the Time Sheet, "Make Invoice" button shall appear.
-
+
-* Click on that button to raise a Sales Invoice against the Time Log Batch.
+* Click on that button to raise a Sales Invoice against the Time Sheet.
-
+
-* When you "Submit" the Sales Invoice, the Sales Invoice number will get updated in the Time Logs and Time Log Batch and their status will change to "Billed".
+* When you "Submit" the Sales Invoice, the Sales Invoice number will get updated in the Time Logs and Time Sheet and their status will change to "Billed".
{next}
\ No newline at end of file
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 047ec8d48c..a6481558e3 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -41,7 +41,7 @@ my_account_context = "erpnext.shopping_cart.utils.update_my_account_context"
email_append_to = ["Job Applicant", "Opportunity", "Issue"]
-calendars = ["Task", "Production Order", "Time Log", "Leave Application", "Sales Order", "Holiday List"]
+calendars = ["Task", "Production Order", "Leave Application", "Sales Order", "Holiday List"]
fixtures = ["Web Form"]
diff --git a/erpnext/hr/doctype/employee/employee_links.py b/erpnext/hr/doctype/employee/employee_links.py
index 0e13c31ccc..2ea6191f34 100644
--- a/erpnext/hr/doctype/employee/employee_links.py
+++ b/erpnext/hr/doctype/employee/employee_links.py
@@ -9,7 +9,7 @@ links = {
},
{
'label': _('Payroll'),
- 'items': ['Salary Structure', 'Salary Slip']
+ 'items': ['Salary Structure', 'Salary Slip', 'Time Sheet']
},
{
'label': _('Expense'),
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json
index 599aaccfa9..ba2c38b0f4 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.json
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.json
@@ -166,7 +166,7 @@
"bold": 0,
"collapsible": 0,
"default": "1",
- "description": "Check if you want to send salary slip in mail to each employee while submitting salary slip",
+ "description": "",
"fieldname": "email_salary_slip_to_employee",
"fieldtype": "Check",
"hidden": 0,
@@ -200,7 +200,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-06-25 17:43:06.643469",
+ "modified": "2016-06-27 16:20:59.737869",
"modified_by": "Administrator",
"module": "HR",
"name": "HR Settings",
@@ -220,8 +220,6 @@
"print": 1,
"read": 1,
"report": 0,
- "restrict": 0,
- "restricted": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.js b/erpnext/hr/doctype/process_payroll/process_payroll.js
index 3da896cfc5..1c60a5fc1d 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.js
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.js
@@ -58,7 +58,8 @@ cur_frm.cscript.make_jv = function(doc, dt, dn) {
});
}
-
-frappe.ui.form.on("Process Payroll", "refresh", function(frm) {
- frm.disable_save();
-});
+frappe.ui.form.on("Process Payroll", {
+ refresh: function(frm) {
+ frm.disable_save();
+ }
+})
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.json b/erpnext/hr/doctype/process_payroll/process_payroll.json
index e3f16a5943..72857656c4 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.json
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.json
@@ -2,6 +2,7 @@
"allow_copy": 1,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2012-03-27 14:35:59",
"custom": 0,
"docstatus": 0,
@@ -180,6 +181,55 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "section_break_8",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "salary_slip_based_on_timesheet",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Salary Slip Based on Timesheet",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
{
"allow_on_submit": 0,
"bold": 0,
@@ -503,13 +553,14 @@
"hide_toolbar": 1,
"icon": "icon-cog",
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-04-26 07:22:41.792785",
+ "modified": "2016-06-22 18:14:02.418857",
"modified_by": "Administrator",
"module": "HR",
"name": "Process Payroll",
@@ -536,8 +587,10 @@
"write": 1
}
],
+ "quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.py b/erpnext/hr/doctype/process_payroll/process_payroll.py
index de41530d26..c80c660098 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.py
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.py
@@ -15,20 +15,18 @@ class ProcessPayroll(Document):
Returns list of active employees based on selected criteria
and for which salary structure exists
"""
-
cond = self.get_filter_condition()
cond += self.get_joining_releiving_condition()
emp_list = frappe.db.sql("""
select t1.name
from `tabEmployee` t1, `tabSalary Structure` t2
- where t1.docstatus!=2 and t2.docstatus != 2
- and t1.name = t2.employee
+ where t1.docstatus!=2 and t2.docstatus != 2 and
+ ifnull(t2.salary_slip_based_on_timesheet,0) = 0 and t1.name = t2.employee
%s """% cond)
return emp_list
-
def get_filter_condition(self):
self.check_mandatory()
@@ -67,6 +65,7 @@ class ProcessPayroll(Document):
""", (emp[0], self.month, self.fiscal_year, self.company)):
ss = frappe.get_doc({
"doctype": "Salary Slip",
+ "salary_slip_based_on_timesheet": 0,
"fiscal_year": self.fiscal_year,
"employee": emp[0],
"month": self.month,
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.js b/erpnext/hr/doctype/salary_slip/salary_slip.js
index 88796a21fe..643139aebf 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.js
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.js
@@ -2,13 +2,51 @@
// License: GNU General Public License v3. See license.txt
cur_frm.add_fetch('employee', 'company', 'company');
+cur_frm.add_fetch('time_sheet', 'total_hours', 'working_hours');
frappe.ui.form.on("Salary Slip", {
+ setup: function(frm) {
+ frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(){
+ return {
+ filters: {
+ employee: frm.doc.employee,
+ make_for: 'Salary Slip'
+ }
+ }
+ }
+ },
+
company: function(frm) {
var company = locals[':Company'][frm.doc.company];
if(!frm.doc.letter_head && company.default_letter_head) {
frm.set_value('letter_head', company.default_letter_head);
}
+ },
+
+ refresh: function(frm) {
+ frm.trigger("toggle_fields")
+ },
+
+ salary_slip_based_on_timesheet: function(frm) {
+ frm.trigger("toggle_fields")
+ },
+
+ toggle_fields: function(frm) {
+ if(frm.doc.salary_slip_based_on_timesheet) {
+ hide_field(['fiscal_year', 'month', 'total_days_in_month', 'leave_without_pay', 'payment_days'])
+ unhide_field(['start_date', 'end_date', 'hourly_wages', 'timesheets'])
+ }else {
+ unhide_field(['fiscal_year', 'month', 'total_days_in_month', 'leave_without_pay', 'payment_days'])
+ hide_field(['start_date', 'end_date', 'hourly_wages', 'timesheets'])
+ }
+ }
+})
+
+
+frappe.ui.form.on("Salary Slip Timesheet", {
+ time_sheet: function(frm, cdt, cdn) {
+ doc = frm.doc;
+ cur_frm.cscript.fiscal_year(doc, cdt, cdn)
}
})
@@ -38,7 +76,13 @@ cur_frm.cscript.fiscal_year = function(doc,dt,dn){
});
}
-cur_frm.cscript.month = cur_frm.cscript.employee = cur_frm.cscript.fiscal_year;
+cur_frm.cscript.month = cur_frm.cscript.salary_slip_based_on_timesheet = cur_frm.cscript.fiscal_year;
+cur_frm.cscript.start_date = cur_frm.cscript.end_date = cur_frm.cscript.fiscal_year;
+
+cur_frm.cscript.employee = function(doc,dt,dn){
+ doc.salary_structure = ''
+ cur_frm.cscript.fiscal_year(doc, dt, dn)
+}
cur_frm.cscript.leave_without_pay = function(doc,dt,dn){
if (doc.employee && doc.fiscal_year && doc.month) {
@@ -56,7 +100,7 @@ var calculate_all = function(doc, dt, dn) {
calculate_net_pay(doc, dt, dn);
}
-cur_frm.cscript.e_modified_amount = function(doc,dt,dn){
+cur_frm.cscript.earning_amount = function(doc,dt,dn){
calculate_earning_total(doc, dt, dn);
calculate_net_pay(doc, dt, dn);
}
@@ -67,7 +111,7 @@ cur_frm.cscript.e_depends_on_lwp = function(doc,dt,dn){
}
// Trigger on earning modified amount and depends on lwp
// ------------------------------------------------------------------------
-cur_frm.cscript.d_modified_amount = function(doc,dt,dn){
+cur_frm.cscript.deduction_amount = function(doc,dt,dn){
calculate_ded_total(doc, dt, dn);
calculate_net_pay(doc, dt, dn);
}
@@ -85,17 +129,17 @@ var calculate_earning_total = function(doc, dt, dn, reset_amount) {
var total_earn = 0;
for(var i = 0; i < tbl.length; i++){
if(cint(tbl[i].e_depends_on_lwp) == 1) {
- tbl[i].e_modified_amount = Math.round(tbl[i].e_amount)*(flt(doc.payment_days) /
+ tbl[i].earning_amount = Math.round(tbl[i].e_amount)*(flt(doc.payment_days) /
cint(doc.total_days_in_month)*100)/100;
- refresh_field('e_modified_amount', tbl[i].name, 'earnings');
+ refresh_field('earning_amount', tbl[i].name, 'earnings');
} else if(reset_amount) {
- tbl[i].e_modified_amount = tbl[i].e_amount;
- refresh_field('e_modified_amount', tbl[i].name, 'earnings');
+ tbl[i].earning_amount = tbl[i].e_amount;
+ refresh_field('earning_amount', tbl[i].name, 'earnings');
}
- total_earn += flt(tbl[i].e_modified_amount);
+ total_earn += flt(tbl[i].earning_amount);
}
doc.gross_pay = total_earn + flt(doc.arrear_amount) + flt(doc.leave_encashment_amount);
- refresh_many(['e_modified_amount', 'gross_pay']);
+ refresh_many(['earning_amount', 'gross_pay']);
}
// Calculate deduction total
@@ -106,13 +150,13 @@ var calculate_ded_total = function(doc, dt, dn, reset_amount) {
var total_ded = 0;
for(var i = 0; i < tbl.length; i++){
if(cint(tbl[i].d_depends_on_lwp) == 1) {
- tbl[i].d_modified_amount = Math.round(tbl[i].d_amount)*(flt(doc.payment_days)/cint(doc.total_days_in_month)*100)/100;
- refresh_field('d_modified_amount', tbl[i].name, 'deductions');
+ tbl[i].deduction_amount = Math.round(tbl[i].d_amount)*(flt(doc.payment_days)/cint(doc.total_days_in_month)*100)/100;
+ refresh_field('deduction_amount', tbl[i].name, 'deductions');
} else if(reset_amount) {
- tbl[i].d_modified_amount = tbl[i].d_amount;
- refresh_field('d_modified_amount', tbl[i].name, 'earnings');
+ tbl[i].deduction_amount = tbl[i].d_amount;
+ refresh_field('deduction_amount', tbl[i].name, 'earnings');
}
- total_ded += flt(tbl[i].d_modified_amount);
+ total_ded += flt(tbl[i].deduction_amount);
}
doc.total_deduction = total_ded;
refresh_field('total_deduction');
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json
index a31e7b0b64..04c214a27e 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.json
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.json
@@ -2,6 +2,7 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-01-10 16:34:15",
"custom": 0,
"docstatus": 0,
@@ -65,7 +66,7 @@
"bold": 0,
"collapsible": 0,
"fieldname": "employee_name",
- "fieldtype": "Data",
+ "fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -76,10 +77,11 @@
"no_copy": 0,
"oldfieldname": "employee_name",
"oldfieldtype": "Data",
+ "options": "employee.employee_name",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
- "read_only": 1,
+ "read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -167,6 +169,31 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break1",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldtype": "Column Break",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0,
+ "width": "50%"
+ },
{
"allow_on_submit": 0,
"bold": 0,
@@ -221,8 +248,8 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "column_break1",
- "fieldtype": "Column Break",
+ "fieldname": "section_break_10",
+ "fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -230,8 +257,8 @@
"in_list_view": 0,
"length": 0,
"no_copy": 0,
- "oldfieldtype": "Column Break",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -239,41 +266,40 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "unique": 0,
- "width": "50%"
+ "unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "month",
- "fieldtype": "Select",
+ "depends_on": "",
+ "fieldname": "salary_slip_based_on_timesheet",
+ "fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 1,
- "in_list_view": 1,
- "label": "Month",
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Salary Slip Based on Timesheet",
"length": 0,
"no_copy": 0,
- "oldfieldname": "month",
- "oldfieldtype": "Select",
- "options": "\n01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12",
+ "options": "",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
- "reqd": 1,
- "search_index": 1,
+ "reqd": 0,
+ "search_index": 0,
"set_only_once": 0,
- "unique": 0,
- "width": "37%"
+ "unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "depends_on": "",
"fieldname": "fiscal_year",
"fieldtype": "Link",
"hidden": 0,
@@ -301,6 +327,139 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "depends_on": "",
+ "fieldname": "month",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 1,
+ "in_list_view": 1,
+ "label": "Month",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "month",
+ "oldfieldtype": "Select",
+ "options": "\n01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 1,
+ "set_only_once": 0,
+ "unique": 0,
+ "width": "37%"
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "start_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Start Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "default": "Today",
+ "depends_on": "",
+ "fieldname": "end_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "End Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_15",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "salary_structure",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Salary Structure",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Salary Structure",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
"fieldname": "total_days_in_month",
"fieldtype": "Float",
"hidden": 0,
@@ -327,6 +486,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "depends_on": "",
"fieldname": "leave_without_pay",
"fieldtype": "Float",
"hidden": 0,
@@ -353,6 +513,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "depends_on": "",
"fieldname": "payment_days",
"fieldtype": "Float",
"hidden": 0,
@@ -375,6 +536,159 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "hourly_wages",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "timesheets",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Salary Slip Timesheet",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Salary Slip Timesheet",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_20",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "total_working_hours",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Total Working Hours",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "hour_rate",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Hour Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "2",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "section_break_26",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
{
"allow_on_submit": 0,
"bold": 0,
@@ -427,6 +741,30 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_01",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
{
"allow_on_submit": 0,
"bold": 0,
@@ -610,52 +948,6 @@
"set_only_once": 0,
"unique": 0
},
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "column_break_25",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "column_break_26",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
{
"allow_on_submit": 0,
"bold": 0,
@@ -737,6 +1029,29 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_25",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
{
"allow_on_submit": 0,
"bold": 0,
@@ -849,13 +1164,14 @@
"hide_toolbar": 0,
"icon": "icon-file-text",
"idx": 9,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
- "max_attachments": 0,
- "modified": "2016-04-26 06:02:06.940543",
+ "max_attachments": 0,
+ "modified": "2016-06-27 16:22:46.063078",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",
@@ -923,6 +1239,7 @@
"write": 0
}
],
+ "quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index d1fac25ef2..d95cbf47ae 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -20,14 +20,16 @@ class SalarySlip(TransactionBase):
self.name = make_autoname('Sal Slip/' +self.employee + '/.#####')
def validate(self):
+ self.validate_dates()
self.check_existing()
+ self.set_month_dates()
if not (len(self.get("earnings")) or len(self.get("deductions"))):
self.get_emp_and_leave_details()
else:
self.get_leave_details(lwp = self.leave_without_pay)
- if not self.net_pay:
+ if self.salary_slip_based_on_timesheet or not self.net_pay:
self.calculate_net_pay()
company_currency = get_company_currency(self.company)
@@ -35,38 +37,85 @@ class SalarySlip(TransactionBase):
set_employee_name(self)
+ def validate_dates(self):
+ if date_diff(self.end_date, self.start_date) < 0:
+ frappe.throw(_("To date cannot be before From date"))
+
def get_emp_and_leave_details(self):
if self.employee:
+ self.set("earnings", [])
+ self.set("deductions", [])
+
+ self.set_month_dates()
+ self.validate_dates()
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
["date_of_joining", "relieving_date"])
self.get_leave_details(joining_date, relieving_date)
-
struct = self.check_sal_struct(joining_date, relieving_date)
+
if struct:
- self.set("earnings", [])
- self.set("deduction", [])
- self.pull_sal_struct(struct)
+ ss_doc = frappe.get_doc('Salary Structure', struct)
+ self.salary_slip_based_on_timesheet = ss_doc.salary_slip_based_on_timesheet or 0
+ self.set_time_sheet()
+ self.pull_sal_struct(ss_doc)
+
+ def set_time_sheet(self):
+ if self.salary_slip_based_on_timesheet and not self.get('timesheets'):
+ self.set("timesheets", [])
+
+ timesheets = frappe.db.sql(""" select * from `tabTime Sheet` where employee = %(employee)s and (status = 'Submitted' or
+ status = 'Billed')""", {'employee': self.employee}, as_dict=1)
+
+ for data in timesheets:
+ self.append('timesheets', {
+ 'time_sheet': data.name,
+ 'working_hours': data.total_hours
+ })
+
+ def set_month_dates(self):
+ if self.month and not self.salary_slip_based_on_timesheet:
+ m = get_month_details(self.fiscal_year, self.month)
+ self.start_date = m['month_start_date']
+ self.end_date = m['month_end_date']
def check_sal_struct(self, joining_date, relieving_date):
- m = get_month_details(self.fiscal_year, self.month)
-
+ timesheet = 1 if self.salary_slip_based_on_timesheet else 0
struct = frappe.db.sql("""select name from `tabSalary Structure`
where employee=%s and is_active = 'Yes'
and (from_date <= %s or from_date <= %s)
- and (to_date is null or to_date >= %s or to_date >= %s)""",
- (self.employee, m.month_start_date, joining_date, m.month_end_date, relieving_date))
+ and (to_date is null or to_date >= %s or to_date >= %s) and salary_slip_based_on_timesheet=%s""",
+ (self.employee, self.start_date, joining_date, self.end_date, relieving_date, timesheet))
if not struct:
- msgprint(_("No active Salary Structure found for employee {0} and the month")
+ self.salary_structure = None
+ msgprint(_("No active or default Salary Structure found for employee {0} and the month")
.format(self.employee))
- self.employee = None
return struct and struct[0][0] or ''
- def pull_sal_struct(self, struct):
+ def pull_sal_struct(self, ss_doc):
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
- make_salary_slip(struct, self)
+ make_salary_slip(ss_doc.name, self)
+
+ if self.salary_slip_based_on_timesheet:
+ self.salary_structure = ss_doc.name
+ self.hour_rate = ss_doc.hour_rate
+ self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0
+ self.add_earning_for_hourly_wages(ss_doc.earning_type)
+
+ def add_earning_for_hourly_wages(self, earning_type):
+ default_type = False
+ for data in self.earnings:
+ if data.earning_type == earning_type:
+ data.earning_amount = self.hour_rate * self.total_working_hours
+ default_type = True
+ break
+
+ if not default_type:
+ earnings = self.append('earnings', {})
+ earnings.earning_type = earning_type
+ earnings.earning_amount = self.hour_rate * self.total_working_hours
def pull_emp_details(self):
emp = frappe.db.get_value("Employee", self.employee, ["bank_name", "bank_ac_no"], as_dict=1)
@@ -81,40 +130,41 @@ class SalarySlip(TransactionBase):
if not self.month:
self.month = "%02d" % getdate(nowdate()).month
+ self.set_month_dates()
if not joining_date:
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
["date_of_joining", "relieving_date"])
- m = get_month_details(self.fiscal_year, self.month)
- holidays = self.get_holidays_for_employee(m['month_start_date'], m['month_end_date'])
+ holidays = self.get_holidays_for_employee(self.start_date, self.end_date)
- working_days = m["month_days"]
+ 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)
if working_days < 0:
frappe.throw(_("There are more holidays than working days this month."))
if not lwp:
- lwp = self.calculate_lwp(holidays, m)
+ lwp = self.calculate_lwp(holidays, working_days)
self.total_days_in_month = working_days
self.leave_without_pay = lwp
- payment_days = flt(self.get_payment_days(m, joining_date, relieving_date)) - flt(lwp)
+ payment_days = flt(self.get_payment_days(joining_date, relieving_date)) - flt(lwp)
self.payment_days = payment_days > 0 and payment_days or 0
- def get_payment_days(self, month, joining_date, relieving_date):
- start_date = month['month_start_date']
+ def get_payment_days(self, joining_date, relieving_date):
+ start_date = getdate(self.start_date)
+
if joining_date:
- if joining_date > month['month_start_date']:
+ if joining_date > getdate(self.start_date):
start_date = joining_date
- elif joining_date > month['month_end_date']:
+ elif joining_date > getdate(self.end_date):
return
- end_date = month['month_end_date']
+ end_date = getdate(self.end_date)
if relieving_date:
- if relieving_date > start_date and relieving_date < month['month_end_date']:
+ if relieving_date > start_date and relieving_date < getdate(self.end_date):
end_date = relieving_date
- elif relieving_date < month['month_start_date']:
+ elif relieving_date < getdate(self.start_date):
frappe.throw(_("Employee relieved on {0} must be set as 'Left'")
.format(relieving_date))
@@ -142,10 +192,10 @@ class SalarySlip(TransactionBase):
return holidays
- def calculate_lwp(self, holidays, m):
+ def calculate_lwp(self, holidays, working_days):
lwp = 0
- for d in range(m['month_days']):
- dt = add_days(cstr(m['month_start_date']), d)
+ for d in range(working_days):
+ dt = add_days(cstr(self.start_date), d)
if dt not in holidays:
leave = frappe.db.sql("""
select t1.name, t1.half_day
@@ -161,38 +211,43 @@ class SalarySlip(TransactionBase):
return lwp
def check_existing(self):
- ret_exist = frappe.db.sql("""select name from `tabSalary Slip`
- where month = %s and fiscal_year = %s and docstatus != 2
- and employee = %s and name != %s""",
- (self.month, self.fiscal_year, self.employee, self.name))
- if ret_exist:
- self.employee = ''
- frappe.throw(_("Salary Slip of employee {0} already created for this month").format(self.employee))
+ if not self.salary_slip_based_on_timesheet:
+ ret_exist = frappe.db.sql("""select name from `tabSalary Slip`
+ where month = %s and fiscal_year = %s and docstatus != 2
+ and employee = %s and name != %s""",
+ (self.month, self.fiscal_year, self.employee, self.name))
+ if ret_exist:
+ self.employee = ''
+ frappe.throw(_("Salary Slip of employee {0} already created for this period").format(self.employee))
+ else:
+ for data in self.timesheets:
+ if frappe.db.get_value('Time Sheet', data.time_sheet, 'status') == 'Payrolled':
+ frappe.throw(_("Salary Slip of employee {0} already created for time sheet {1}").format(self.employee, data.time_sheet))
def calculate_earning_total(self):
self.gross_pay = flt(self.arrear_amount) + flt(self.leave_encashment_amount)
for d in self.get("earnings"):
if cint(d.e_depends_on_lwp) == 1:
- d.e_modified_amount = rounded((flt(d.e_amount) * flt(self.payment_days)
- / cint(self.total_days_in_month)), self.precision("e_modified_amount", "earnings"))
+ d.earning_amount = rounded((flt(d.e_amount) * flt(self.payment_days)
+ / cint(self.total_days_in_month)), self.precision("earning_amount", "earnings"))
elif not self.payment_days:
- d.e_modified_amount = 0
- elif not d.e_modified_amount:
- d.e_modified_amount = d.e_amount
- self.gross_pay += flt(d.e_modified_amount)
+ d.earning_amount = 0
+ elif not d.earning_amount:
+ d.earning_amount = d.e_amount
+ self.gross_pay += flt(d.earning_amount)
def calculate_ded_total(self):
self.total_deduction = 0
for d in self.get('deductions'):
if cint(d.d_depends_on_lwp) == 1:
- d.d_modified_amount = rounded((flt(d.d_amount) * flt(self.payment_days)
- / cint(self.total_days_in_month)), self.precision("d_modified_amount", "deductions"))
+ d.deduction_amount = rounded((flt(d.d_amount) * flt(self.payment_days)
+ / cint(self.total_days_in_month)), self.precision("deduction_amount", "deductions"))
elif not self.payment_days:
- d.d_modified_amount = 0
- elif not d.d_modified_amount:
- d.d_modified_amount = d.d_amount
+ d.deduction_amount = 0
+ elif not d.deduction_amount:
+ d.deduction_amount = d.d_amount
- self.total_deduction += flt(d.d_modified_amount)
+ self.total_deduction += flt(d.deduction_amount)
def calculate_net_pay(self):
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))
@@ -204,16 +259,28 @@ class SalarySlip(TransactionBase):
self.precision("net_pay") if disable_rounded_total else 0)
def on_submit(self):
+ self.update_status(self.name)
if(frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")):
self.email_salary_slip()
-
+
+ def on_cancel(self):
+ self.update_status()
def email_salary_slip(self):
receiver = frappe.db.get_value("Employee", self.employee, "company_email") or \
frappe.db.get_value("Employee", self.employee, "personal_email")
if receiver:
- subj = 'Salary Slip - ' + cstr(self.month) +'/'+cstr(self.fiscal_year)
+ subj = 'Salary Slip - from {0} to {1}, fiscal year {2}'.format(self.start_date, self.end_date, self.fiscal_year)
frappe.sendmail([receiver], subject=subj, message = _("Please see attachment"),
attachments=[frappe.attach_print(self.doctype, self.name, file_name=self.name)], reference_doctype= self.doctype, reference_name= self.name)
else:
msgprint(_("{0}: Employee email not found, hence email not sent").format(self.employee_name))
+
+ def update_status(self, salary_slip=None):
+ for data in self.timesheets:
+ if data.time_sheet:
+ timesheet = frappe.get_doc('Time Sheet', data.time_sheet)
+ timesheet.salary_slip = salary_slip
+ timesheet.flags.ignore_validate_update_after_submit = True
+ timesheet.set_status()
+ timesheet.save()
\ No newline at end of file
diff --git a/erpnext/hr/doctype/salary_slip/test_records.json b/erpnext/hr/doctype/salary_slip/test_records.json
index da8d95d0ba..6e978a68af 100644
--- a/erpnext/hr/doctype/salary_slip/test_records.json
+++ b/erpnext/hr/doctype/salary_slip/test_records.json
@@ -5,14 +5,14 @@
{
"d_amount": 100,
"d_depends_on_lwp": 0,
- "d_type": "_Test Professional Tax",
+ "deduction_type": "_Test Professional Tax",
"doctype": "Salary Slip Deduction",
"parentfield": "deductions"
},
{
"d_amount": 50,
"d_depends_on_lwp": 1,
- "d_type": "_Test TDS",
+ "deduction_type": "_Test TDS",
"doctype": "Salary Slip Deduction",
"parentfield": "deductions"
}
@@ -23,14 +23,14 @@
"doctype": "Salary Slip Earning",
"e_amount": 15000,
"e_depends_on_lwp": 1,
- "e_type": "_Test Basic Salary",
+ "earning_type": "_Test Basic Salary",
"parentfield": "earnings"
},
{
"doctype": "Salary Slip Earning",
"e_amount": 500,
"e_depends_on_lwp": 0,
- "e_type": "_Test Allowance",
+ "earning_type": "_Test Allowance",
"parentfield": "earnings"
}
],
diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py
index 4ddaecc80e..4facd50e99 100644
--- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py
@@ -4,7 +4,7 @@ from __future__ import unicode_literals
import unittest
import frappe
-from frappe.utils import today
+from frappe.utils import today, now_datetime, getdate, cstr
from erpnext.hr.doctype.employee.employee import make_salary_structure
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
from erpnext.hr.doctype.leave_application.test_leave_application import make_allocation_record
@@ -35,10 +35,10 @@ class TestSalarySlip(unittest.TestCase):
self.assertEquals(ss.total_days_in_month, 31)
self.assertEquals(ss.payment_days, 30)
- self.assertEquals(ss.earnings[0].e_modified_amount, 14516.13)
- self.assertEquals(ss.earnings[1].e_modified_amount, 500)
- self.assertEquals(ss.deductions[0].d_modified_amount, 100)
- self.assertEquals(ss.deductions[1].d_modified_amount, 48.39)
+ self.assertEquals(ss.earnings[0].earning_amount, 14516.13)
+ self.assertEquals(ss.earnings[1].earning_amount, 500)
+ self.assertEquals(ss.deductions[0].deduction_amount, 100)
+ self.assertEquals(ss.deductions[1].deduction_amount, 48.39)
self.assertEquals(ss.gross_pay, 15016.13)
self.assertEquals(ss.net_pay, 14867.74)
@@ -49,10 +49,10 @@ class TestSalarySlip(unittest.TestCase):
self.assertEquals(ss.total_days_in_month, 29)
self.assertEquals(ss.payment_days, 28)
- self.assertEquals(ss.earnings[0].e_modified_amount, 14482.76)
- self.assertEquals(ss.earnings[1].e_modified_amount, 500)
- self.assertEquals(ss.deductions[0].d_modified_amount, 100)
- self.assertEquals(ss.deductions[1].d_modified_amount, 48.28)
+ self.assertEquals(ss.earnings[0].earning_amount, 14482.76)
+ self.assertEquals(ss.earnings[1].earning_amount, 500)
+ self.assertEquals(ss.deductions[0].deduction_amount, 100)
+ self.assertEquals(ss.deductions[1].deduction_amount, 48.28)
self.assertEquals(ss.gross_pay, 14982.76)
self.assertEquals(ss.net_pay, 14834.48)
@@ -153,6 +153,13 @@ class TestSalarySlip(unittest.TestCase):
return salary_slip
+ def make_activity_for_employee(self):
+ activity_type = frappe.get_doc("Activity Type", "_Test Activity Type")
+ activity_type.billing_rate = 50
+ activity_type.costing_rate = 20
+ activity_type.wage_rate = 25
+ activity_type.save()
+
test_dependencies = ["Leave Application", "Holiday List"]
test_records = frappe.get_test_records('Salary Slip')
diff --git a/erpnext/hr/doctype/salary_slip_deduction/salary_slip_deduction.json b/erpnext/hr/doctype/salary_slip_deduction/salary_slip_deduction.json
index a0e722ed0a..72fd6579d6 100644
--- a/erpnext/hr/doctype/salary_slip_deduction/salary_slip_deduction.json
+++ b/erpnext/hr/doctype/salary_slip_deduction/salary_slip_deduction.json
@@ -2,19 +2,22 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-02-22 01:27:48",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
+ "document_type": "Setup",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "d_type",
+ "fieldname": "deduction_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Type",
@@ -25,6 +28,7 @@
"options": "Deduction Type",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "200px",
"read_only": 0,
"report_hide": 0,
@@ -42,6 +46,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Default Amount",
@@ -52,6 +57,7 @@
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 1,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -67,6 +73,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Depends on Leave Without Pay",
@@ -74,6 +81,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 1,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -85,10 +93,11 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "d_modified_amount",
+ "fieldname": "deduction_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Amount",
@@ -97,6 +106,7 @@
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -108,18 +118,22 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2015-11-16 06:29:55.610195",
+ "modified": "2016-06-27 16:02:25.613138",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip Deduction",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 1,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "sort_order": "ASC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/salary_slip_earning/salary_slip_earning.json b/erpnext/hr/doctype/salary_slip_earning/salary_slip_earning.json
index 6d897b0259..3301dd8486 100644
--- a/erpnext/hr/doctype/salary_slip_earning/salary_slip_earning.json
+++ b/erpnext/hr/doctype/salary_slip_earning/salary_slip_earning.json
@@ -2,19 +2,22 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-02-22 01:27:48",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
+ "document_type": "Setup",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "e_type",
+ "fieldname": "earning_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Type",
@@ -25,6 +28,7 @@
"options": "Earning Type",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "",
"read_only": 0,
"report_hide": 0,
@@ -42,6 +46,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Default Amount",
@@ -52,6 +57,7 @@
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 1,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -67,6 +73,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Depends on Leave Without Pay",
@@ -74,6 +81,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 1,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -85,10 +93,11 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "e_modified_amount",
+ "fieldname": "earning_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Amount",
@@ -97,6 +106,7 @@
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -108,18 +118,22 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2015-11-16 06:29:55.649418",
+ "modified": "2016-06-27 15:56:04.146109",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip Earning",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 1,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "sort_order": "ASC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/projects/doctype/time_log_batch/__init__.py b/erpnext/hr/doctype/salary_slip_timesheet/__init__.py
similarity index 100%
rename from erpnext/projects/doctype/time_log_batch/__init__.py
rename to erpnext/hr/doctype/salary_slip_timesheet/__init__.py
diff --git a/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json b/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json
new file mode 100644
index 0000000000..a503e7342e
--- /dev/null
+++ b/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json
@@ -0,0 +1,87 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2016-06-14 19:22:29.811658",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "time_sheet",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Time Sheet",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Time Sheet",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "working_hours",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Working Hours",
+ "length": 0,
+ "no_copy": 1,
+ "permlevel": 0,
+ "precision": "3",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2016-06-15 17:59:57.876373",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Salary Slip Timesheet",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.py b/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.py
new file mode 100644
index 0000000000..1bbfc53a32
--- /dev/null
+++ b/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class SalarySlipTimesheet(Document):
+ pass
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js
index 77f516eff7..66680672c6 100755
--- a/erpnext/hr/doctype/salary_structure/salary_structure.js
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.js
@@ -12,7 +12,7 @@ cur_frm.cscript.onload = function(doc, dt, dn){
}
cur_frm.cscript.refresh = function(doc, dt, dn){
- if((!doc.__islocal) && (doc.is_active == 'Yes')){
+ if((!doc.__islocal) && (doc.is_active == 'Yes') && (doc.salary_slip_based_on_timesheet == 0)){
cur_frm.add_custom_button(__('Salary Slip'),
cur_frm.cscript['Make Salary Slip'], __("Make"));
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
@@ -52,7 +52,11 @@ var calculate_totals = function(doc, cdt, cdn) {
}
doc.total_earning = total_earn;
doc.total_deduction = total_ded;
- doc.net_pay = flt(total_earn) - flt(total_ded);
+ doc.net_pay = 0.0
+ if(doc.salary_slip_based_on_timesheet == 0){
+ doc.net_pay = flt(total_earn) - flt(total_ded);
+ }
+
refresh_many(['total_earning', 'total_deduction', 'net_pay']);
}
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.json b/erpnext/hr/doctype/salary_structure/salary_structure.json
index e832697cd2..9a68f9d6c3 100644
--- a/erpnext/hr/doctype/salary_structure/salary_structure.json
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.json
@@ -2,6 +2,7 @@
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-03-07 18:50:29",
"custom": 0,
"docstatus": 0,
@@ -269,6 +270,33 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "default": "No",
+ "fieldname": "is_default",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Is Default",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Yes\nNo",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
{
"allow_on_submit": 0,
"bold": 0,
@@ -325,6 +353,136 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "depends_on": "eval:!doc.__islocal",
+ "fieldname": "salary_slip_based_on_timesheet",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Salary Slip Based on Timesheet",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "eval:doc.salary_slip_based_on_timesheet",
+ "fieldname": "section_break_15",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "description": "Earning type for timesheet based payroll.",
+ "fieldname": "earning_type",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Earning Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Earning Type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_17",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "hour_rate",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Hour Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "2",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
"description": "Salary breakup based on Earning and Deduction.",
"fieldname": "earning_deduction",
"fieldtype": "Section Break",
@@ -333,7 +491,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Monthly Earning & Deduction",
+ "label": "",
"length": 0,
"no_copy": 0,
"oldfieldname": "earning_deduction",
@@ -379,6 +537,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "depends_on": "",
"fieldname": "earnings",
"fieldtype": "Table",
"hidden": 0,
@@ -397,7 +556,7 @@
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
- "reqd": 1,
+ "reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
@@ -460,7 +619,8 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "section_break0",
+ "depends_on": "",
+ "fieldname": "net_pay_detail",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -612,13 +772,14 @@
"hide_toolbar": 0,
"icon": "icon-file-text",
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-03-18 16:10:30.031811",
+ "modified": "2016-06-27 16:23:16.856237",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure",
@@ -649,7 +810,7 @@
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
- "delete": 0,
+ "delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
@@ -665,10 +826,12 @@
"write": 1
}
],
+ "quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"timeline_field": "employee",
- "title_field": "employee_name"
+ "title_field": "employee_name",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py
index 69f341bb3c..4538ff79c4 100644
--- a/erpnext/hr/doctype/salary_structure/salary_structure.py
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.py
@@ -16,7 +16,7 @@ class SalaryStructure(Document):
self.name = make_autoname(self.employee + '/.SST' + '/.#####')
def validate(self):
- self.check_existing()
+ self.check_overlap()
self.validate_amount()
self.validate_employee()
self.validate_joining_date()
@@ -48,43 +48,44 @@ class SalaryStructure(Document):
for li in list1:
child = self.append(tab_fname, {})
if(tab_fname == 'earnings'):
- child.e_type = cstr(li[0])
+ child.earning_type = cstr(li[0])
child.modified_value = 0
elif(tab_fname == 'deductions'):
- child.d_type = cstr(li[0])
+ child.deduction_type = cstr(li[0])
child.d_modified_amt = 0
def make_earn_ded_table(self):
self.make_table('Earning Type','earnings','Salary Structure Earning')
self.make_table('Deduction Type','deductions', 'Salary Structure Deduction')
- def check_existing(self):
- ret = self.get_other_active_salary_structure()
-
- if ret and self.is_active=='Yes':
- frappe.throw(_("Another Salary Structure {0} is active for employee {1}. Please make its status 'Inactive' to proceed.").format(ret, self.employee))
-
- def get_other_active_salary_structure(self):
- ret = frappe.db.sql("""select name from `tabSalary Structure` where is_active = 'Yes'
- and employee = %s and name!=%s""", (self.employee,self.name))
-
- return ret[0][0] if ret else None
-
- def before_test_insert(self):
- """Make any existing salary structure for employee inactive."""
- ret = self.get_other_active_salary_structure()
- if ret:
- frappe.db.set_value("Salary Structure", ret, "is_active", "No")
+ def check_overlap(self):
+ existing = frappe.db.sql("""select name from `tabSalary Structure`
+ where employee = %(employee)s and
+ (
+ (%(from_date)s > from_date and %(from_date)s < to_date) or
+ (%(to_date)s > from_date and %(to_date)s < to_date) or
+ (%(from_date)s <= from_date and %(to_date)s >= to_date))
+ and name!=%(name)s
+ and docstatus < 2""",
+ {
+ "employee": self.employee,
+ "from_date": self.from_date,
+ "to_date": self.to_date,
+ "name": self.name or "No Name"
+ }, as_dict=True)
+
+ if existing:
+ frappe.throw(_("Salary structure {0} already exist, more than one salary structure for same period is not allowed").format(existing[0].name))
def validate_amount(self):
- if flt(self.net_pay) < 0:
+ if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet:
frappe.throw(_("Net pay cannot be negative"))
def validate_employee(self):
old_employee = frappe.db.get_value("Salary Structure", self.name, "employee")
if old_employee and self.employee != old_employee:
frappe.throw(_("Employee can not be changed"))
-
+
def validate_joining_date(self):
joining_date = getdate(frappe.db.get_value("Employee", self.employee, "date_of_joining"))
if getdate(self.from_date) < joining_date:
@@ -93,6 +94,7 @@ class SalaryStructure(Document):
@frappe.whitelist()
def make_salary_slip(source_name, target_doc=None):
def postprocess(source, target):
+ target.salary_structure = source.name
target.run_method("pull_emp_details")
target.run_method("get_leave_details")
target.run_method("calculate_net_pay")
@@ -109,7 +111,7 @@ def make_salary_slip(source_name, target_doc=None):
"field_map": [
["depend_on_lwp", "d_depends_on_lwp"],
["d_modified_amt", "d_amount"],
- ["d_modified_amt", "d_modified_amount"]
+ ["d_modified_amt", "deduction_amount"]
],
"add_if_empty": True
},
@@ -117,8 +119,8 @@ def make_salary_slip(source_name, target_doc=None):
"doctype": "Salary Slip Earning",
"field_map": [
["depend_on_lwp", "e_depends_on_lwp"],
- ["modified_value", "e_modified_amount"],
- ["modified_value", "e_amount"]
+ ["modified_value", "e_amount"],
+ ["modified_value", "earning_amount"]
],
"add_if_empty": True
}
diff --git a/erpnext/hr/doctype/salary_structure/test_records.json b/erpnext/hr/doctype/salary_structure/test_records.json
index 3af83f10b3..b0272da93c 100644
--- a/erpnext/hr/doctype/salary_structure/test_records.json
+++ b/erpnext/hr/doctype/salary_structure/test_records.json
@@ -6,10 +6,10 @@
"from_date": "2014-02-01",
"earnings": [
{
- "e_type": "_Test Basic Salary"
+ "earning_type": "_Test Basic Salary"
},
{
- "e_type": "_Test Allowance"
+ "earning_type": "_Test Allowance"
}
]
}
diff --git a/erpnext/hr/doctype/salary_structure_deduction/salary_structure_deduction.json b/erpnext/hr/doctype/salary_structure_deduction/salary_structure_deduction.json
index 3a718aaadb..fa067768be 100644
--- a/erpnext/hr/doctype/salary_structure_deduction/salary_structure_deduction.json
+++ b/erpnext/hr/doctype/salary_structure_deduction/salary_structure_deduction.json
@@ -2,19 +2,22 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-02-22 01:27:48",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
+ "document_type": "Setup",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "d_type",
+ "fieldname": "deduction_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Type",
@@ -25,6 +28,7 @@
"options": "Deduction Type",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "200px",
"read_only": 0,
"report_hide": 0,
@@ -42,6 +46,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Amount",
@@ -52,6 +57,7 @@
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -67,6 +73,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Reduce Deduction for Leave Without Pay (LWP)",
@@ -76,6 +83,7 @@
"oldfieldtype": "Check",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -87,18 +95,21 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2015-11-16 06:29:55.793274",
+ "modified": "2016-06-27 15:36:26.491643",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure Deduction",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 1,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/salary_structure_earning/salary_structure_earning.json b/erpnext/hr/doctype/salary_structure_earning/salary_structure_earning.json
index 8dfcb0a675..2cbd8b9ea9 100644
--- a/erpnext/hr/doctype/salary_structure_earning/salary_structure_earning.json
+++ b/erpnext/hr/doctype/salary_structure_earning/salary_structure_earning.json
@@ -2,6 +2,7 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-02-22 01:27:48",
"custom": 0,
"docstatus": 0,
@@ -11,10 +12,11 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "e_type",
+ "fieldname": "earning_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Type",
@@ -25,6 +27,7 @@
"options": "Earning Type",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "200px",
"read_only": 0,
"report_hide": 0,
@@ -42,6 +45,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Amount",
@@ -52,6 +56,7 @@
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -67,6 +72,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Reduce Earning for Leave Without Pay (LWP)",
@@ -76,6 +82,7 @@
"oldfieldtype": "Check",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -87,18 +94,22 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2015-11-16 06:29:55.830492",
+ "modified": "2016-06-27 15:28:58.884891",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure Earning",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 1,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "sort_order": "ASC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/hr/report/monthly_salary_register/monthly_salary_register.py b/erpnext/hr/report/monthly_salary_register/monthly_salary_register.py
index 8b023488d5..da5d0fd11d 100644
--- a/erpnext/hr/report/monthly_salary_register/monthly_salary_register.py
+++ b/erpnext/hr/report/monthly_salary_register/monthly_salary_register.py
@@ -42,12 +42,12 @@ def get_columns(salary_slips):
_("Payment Days") + ":Float:120"
]
- earning_types = frappe.db.sql_list("""select distinct e_type from `tabSalary Slip Earning`
- where e_modified_amount != 0 and parent in (%s)""" %
+ earning_types = frappe.db.sql_list("""select distinct earning_type from `tabSalary Slip Earning`
+ where earning_amount != 0 and parent in (%s)""" %
(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]))
- ded_types = frappe.db.sql_list("""select distinct d_type from `tabSalary Slip Deduction`
- where d_modified_amount != 0 and parent in (%s)""" %
+ ded_types = frappe.db.sql_list("""select distinct deduction_type from `tabSalary Slip Deduction`
+ where deduction_amount != 0 and parent in (%s)""" %
(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]))
columns = columns + [(e + ":Currency:120") for e in earning_types] + \
@@ -83,25 +83,25 @@ def get_conditions(filters):
return conditions, filters
def get_ss_earning_map(salary_slips):
- ss_earnings = frappe.db.sql("""select parent, e_type, e_modified_amount
+ ss_earnings = frappe.db.sql("""select parent, earning_type, earning_amount
from `tabSalary Slip Earning` where parent in (%s)""" %
(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1)
ss_earning_map = {}
for d in ss_earnings:
- ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.e_type, [])
- ss_earning_map[d.parent][d.e_type] = flt(d.e_modified_amount)
+ ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.earning_type, [])
+ ss_earning_map[d.parent][d.earning_type] = flt(d.earning_amount)
return ss_earning_map
def get_ss_ded_map(salary_slips):
- ss_deductions = frappe.db.sql("""select parent, d_type, d_modified_amount
+ ss_deductions = frappe.db.sql("""select parent, deduction_type, deduction_amount
from `tabSalary Slip Deduction` where parent in (%s)""" %
(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1)
ss_ded_map = {}
for d in ss_deductions:
- ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.d_type, [])
- ss_ded_map[d.parent][d.d_type] = flt(d.d_modified_amount)
+ ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.deduction_type, [])
+ ss_ded_map[d.parent][d.deduction_type] = flt(d.deduction_amount)
return ss_ded_map
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.js b/erpnext/manufacturing/doctype/production_order/production_order.js
index ba7026eff6..00ce673d84 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.js
+++ b/erpnext/manufacturing/doctype/production_order/production_order.js
@@ -16,6 +16,10 @@ frappe.ui.form.on("Production Order", {
});
erpnext.production_order.set_default_warehouse(frm);
}
+
+ // formatter for production order operation
+ frm.set_indicator_formatter('operation',
+ function(doc) { return (frm.doc.qty==doc.completed_qty) ? "green" : "orange" })
erpnext.production_order.set_custom_buttons(frm);
erpnext.production_order.setup_company_filter(frm);
@@ -33,6 +37,15 @@ frappe.ui.form.on("Production Order", {
if (frm.doc.docstatus===1) {
frm.trigger('show_progress');
}
+
+ if(frm.doc.docstatus == 1){
+ frm.add_custom_button(__('Make Timesheet'), function(){
+ frappe.model.open_mapped_doc({
+ method: "erpnext.manufacturing.doctype.production_order.production_order.make_timesheet",
+ frm: cur_frm
+ })
+ })
+ }
},
show_progress: function(frm) {
var bars = [];
@@ -90,7 +103,7 @@ frappe.ui.form.on("Production Order Operation", {
time_in_mins: function(frm, cdt, cdn) {
erpnext.production_order.calculate_cost(frm.doc);
erpnext.production_order.calculate_total_cost(frm);
- }
+ },
});
erpnext.production_order = {
@@ -112,9 +125,9 @@ erpnext.production_order = {
// opertions
if ((doc.operations || []).length) {
- frm.add_custom_button(__('Time Logs'), function() {
+ frm.add_custom_button(__('Time Sheet'), function() {
frappe.route_options = {"production_order": frm.doc.name};
- frappe.set_route("List", "Time Log");
+ frappe.set_route("List", "Time Sheet");
}, __("View"));
}
@@ -252,32 +265,6 @@ $.extend(cur_frm.cscript, {
qty: function() {
frappe.ui.form.trigger("Production Order", 'bom_no')
},
- show_time_logs: function(doc, cdt, cdn) {
- var child = locals[cdt][cdn]
- frappe.route_options = {"operation_id": child.name};
- frappe.set_route("List", "Time Log");
- },
-
- make_time_log: function(doc, cdt, cdn){
- var child = locals[cdt][cdn]
- frappe.call({
- method:"erpnext.manufacturing.doctype.production_order.production_order.make_time_log",
- args: {
- "name": doc.name,
- "operation": child.operation,
- "from_time": child.planned_start_time,
- "to_time": child.planned_end_time,
- "project": doc.project,
- "workstation": child.workstation,
- "qty": flt(doc.qty) - flt(child.completed_qty),
- "operation_id": child.name
- },
- callback: function(r) {
- var doclist = frappe.model.sync(r.message);
- frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
- }
- });
- }
});
cur_frm.cscript['Stop Production Order'] = function() {
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.json b/erpnext/manufacturing/doctype/production_order/production_order.json
index 7088126c64..447ed0abc1 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.json
+++ b/erpnext/manufacturing/doctype/production_order/production_order.json
@@ -1084,13 +1084,14 @@
"hide_toolbar": 0,
"icon": "icon-cogs",
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-05-11 12:17:29.480533",
+ "modified": "2016-06-23 10:20:22.075476",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Order",
@@ -1115,6 +1116,26 @@
"share": 1,
"submit": 1,
"write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 0,
+ "delete": 0,
+ "email": 0,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 0,
+ "read": 1,
+ "report": 1,
+ "role": "Stock User",
+ "set_user_permissions": 0,
+ "share": 0,
+ "submit": 0,
+ "write": 0
}
],
"quick_entry": 1,
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py
index 6c8dfd5a6d..e10459fcf1 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.py
+++ b/erpnext/manufacturing/doctype/production_order/production_order.py
@@ -4,14 +4,16 @@
from __future__ import unicode_literals
import frappe
+import json
from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate
from frappe import _
from frappe.model.document import Document
+from frappe.model.mapper import get_mapped_doc
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
from dateutil.relativedelta import relativedelta
from erpnext.stock.doctype.item.item import validate_end_of_life
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError, NotInWorkingHoursError
-from erpnext.projects.doctype.time_log.time_log import OverlapError
+from erpnext.projects.doctype.time_sheet.time_sheet import OverlapError
from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
@@ -158,6 +160,11 @@ class ProductionOrder(Document):
def before_submit(self):
self.set_required_items()
+ self.make_timesheets()
+
+ def before_cancel(self):
+ for data in self.operations:
+ data.time_sheet = None
def on_submit(self):
if not self.wip_warehouse:
@@ -166,7 +173,6 @@ class ProductionOrder(Document):
frappe.throw(_("For Warehouse is required before Submit"))
self.update_reserved_qty_for_production()
- self.make_time_logs()
self.update_completed_qty_in_material_request()
self.update_planned_qty()
@@ -175,7 +181,7 @@ class ProductionOrder(Document):
frappe.db.set(self,'status', 'Cancelled')
self.clear_required_items()
- self.delete_time_logs()
+ self.delete_time_sheet()
self.update_completed_qty_in_material_request()
self.update_planned_qty()
@@ -233,8 +239,8 @@ class ProductionOrder(Document):
holidays[holiday_list] = holiday_list_days
return holidays[holiday_list]
-
- def make_time_logs(self):
+
+ def make_timesheets(self):
"""Capacity Planning. Plan time logs based on earliest availablity of workstation after
Planned Start Date. Time logs will be created and remain in Draft mode and must be submitted
before manufacturing entry can be made."""
@@ -242,72 +248,80 @@ class ProductionOrder(Document):
if not self.operations:
return
- time_logs = []
+ time_sheets = []
plan_days = frappe.db.get_single_value("Manufacturing Settings", "capacity_planning_for_days") or 30
+
+ time_sheet = make_time_sheet(self.name)
+ workstation_list = []
+ last_workstation_idx = {}
for i, d in enumerate(self.operations):
- self.set_operation_start_end_time(i, d)
-
- time_log = make_time_log(self.name, d.operation, d.planned_start_time, d.planned_end_time,
- flt(self.qty) - flt(d.completed_qty), self.project, d.workstation, operation_id=d.name)
-
if d.workstation:
+ last_workstation_idx[d.workstation] = i # set last row index of workstation
+ self.set_start_end_time_for_workstation(d, workstation_list, last_workstation_idx.get(d.workstation))
+
+ args = self.get_operations_data(d)
+ add_timesheet_detail(time_sheet, args)
+ original_start_time = d.planned_start_time
+
# validate operating hours if workstation [not mandatory] is specified
self.check_operation_fits_in_working_hours(d)
-
- original_start_time = time_log.from_time
- while True:
- _from_time = time_log.from_time
-
try:
- time_log.save()
- break
- except WorkstationHolidayError:
- time_log.move_to_next_day()
- except NotInWorkingHoursError:
- time_log.move_to_next_working_slot()
+ time_sheet.validate_timesheets()
except OverlapError:
- time_log.move_to_next_non_overlapping_slot()
+ time_sheet.move_to_next_non_overlapping_slot(d.idx)
- # reset end time
- time_log.to_time = get_datetime(time_log.from_time) + relativedelta(minutes=d.time_in_mins)
+ from_time, to_time = self.get_start_end_time(time_sheet, d.name)
- if date_diff(time_log.from_time, original_start_time) > plan_days:
- frappe.msgprint(_("Unable to find Time Slot in the next {0} days for Operation {1}").format(plan_days, d.operation))
+ if date_diff(from_time, original_start_time) > plan_days:
+ frappe.throw(_("Unable to find Time Slot in the next {0} days for Operation {1}").format(plan_days, d.operation))
break
- # if time log needs to be moved, make sure that the from time is not the same
- if _from_time == time_log.from_time:
- frappe.throw(_("Capacity Planning Error"))
+ d.planned_start_time = from_time
+ d.planned_end_time = to_time
+ d.db_update()
- d.planned_start_time = time_log.from_time
- d.planned_end_time = time_log.to_time
- d.db_update()
-
- if time_log.name:
- time_logs.append(time_log.name)
+ if time_sheet:
+ time_sheet.save()
+ time_sheets.append(time_sheet.name)
self.planned_end_date = self.operations[-1].planned_end_time
-
- if time_logs:
+ if time_sheets:
frappe.local.message_log = []
- frappe.msgprint(_("Time Logs created:") + "\n" + "\n".join(time_logs))
+ frappe.msgprint(_("Time Sheet created:") + "\n" + "\n".join(time_sheets))
- def set_operation_start_end_time(self, i, d):
+ def get_operations_data(self, data):
+ return {
+ 'from_time': data.planned_start_time,
+ 'hours': data.time_in_mins / 60,
+ 'to_time': data.planned_end_time,
+ 'project': self.project,
+ 'operation': data.operation,
+ 'operation_id': data.name,
+ 'workstation': data.workstation,
+ 'completed_qty': flt(self.qty) - flt(data.completed_qty)
+ }
+
+ def set_start_end_time_for_workstation(self, data, workstation_list, index):
"""Set start and end time for given operation. If first operation, set start as
`planned_start_date`, else add time diff to end time of earlier operation."""
- if self.planned_start_date:
- if i==0:
- # first operation at planned_start date
- d.planned_start_time = self.planned_start_date
- else:
- d.planned_start_time = get_datetime(self.operations[i-1].planned_end_time)\
- + get_mins_between_operations()
- d.planned_end_time = get_datetime(d.planned_start_time) + relativedelta(minutes = d.time_in_mins)
+ if data.workstation not in workstation_list:
+ data.planned_start_time = self.planned_start_date
+ workstation_list.append(data.workstation)
+ else:
+ data.planned_start_time = get_datetime(self.operations[index-1].planned_end_time)\
+ + get_mins_between_operations()
- if d.planned_start_time == d.planned_end_time:
- frappe.throw(_("Capacity Planning Error"))
+ data.planned_end_time = get_datetime(data.planned_start_time) + relativedelta(minutes = data.time_in_mins)
+
+ if data.planned_start_time == data.planned_end_time:
+ frappe.throw(_("Capacity Planning Error"))
+
+ def get_start_end_time(self, time_sheet, operation_id):
+ for data in time_sheet.timesheets:
+ if data.operation_id == operation_id:
+ return data.from_time, data.to_time
def check_operation_fits_in_working_hours(self, d):
"""Raises expection if operation is longer than working hours in the given workstation."""
@@ -336,9 +350,9 @@ class ProductionOrder(Document):
self.actual_start_date = None
self.actual_end_date = None
- def delete_time_logs(self):
- for time_log in frappe.get_all("Time Log", ["name"], {"production_order": self.name}):
- frappe.delete_doc("Time Log", time_log.name)
+ def delete_time_sheet(self):
+ for time_sheet in frappe.get_all("Time Sheet", ["name"], {"production_order": self.name}):
+ frappe.delete_doc("Time Sheet", time_sheet.name)
def validate_production_item(self):
if frappe.db.get_value("Item", self.production_item, "has_variants"):
@@ -490,22 +504,22 @@ def get_events(start, end, filters=None):
return data
@frappe.whitelist()
-def make_time_log(name, operation, from_time=None, to_time=None, qty=None, project=None, workstation=None, operation_id=None):
- time_log = frappe.new_doc("Time Log")
- time_log.for_manufacturing = 1
- time_log.from_time = from_time
- time_log.to_time = to_time
- time_log.production_order = name
- time_log.project = project
- time_log.operation_id = operation_id
- time_log.operation = operation
- time_log.workstation= workstation
- time_log.activity_type= "Manufacturing"
- time_log.completed_qty = flt(qty)
+def make_time_sheet(production_order):
+ time_sheet = frappe.new_doc("Time Sheet")
+ time_sheet.employee = ""
+ time_sheet.production_order = production_order
+ return time_sheet
- if from_time and to_time :
- time_log.calculate_total_hours()
- return time_log
+@frappe.whitelist()
+def add_timesheet_detail(time_sheet, args):
+ if isinstance(time_sheet, unicode):
+ time_sheet = frappe.get_doc('Time Sheet', time_sheet)
+
+ if isinstance(args, unicode):
+ args = json.loads(args)
+
+ time_sheet.append('timesheets', args)
+ return time_sheet
@frappe.whitelist()
def get_default_warehouse():
@@ -514,3 +528,33 @@ def get_default_warehouse():
fg_warehouse = frappe.db.get_single_value("Manufacturing Settings",
"default_fg_warehouse")
return {"wip_warehouse": wip_warehouse, "fg_warehouse": fg_warehouse}
+
+@frappe.whitelist()
+def make_timesheet(source_name, target_doc=None):
+ def postprocess(source, target):
+ target.production_order = source.name
+ target.naming_series = 'TS-'
+
+ def update_item(source, target, source_parent):
+ target.completed_qty = source_parent.qty - source.completed_qty
+
+ doc = get_mapped_doc("Production Order", source_name, {
+ "Production Order": {
+ "doctype": "Time Sheet",
+ "validation": {
+ "docstatus": ["=", 1]
+ }
+ },
+ "Production Order Operation": {
+ "doctype": "Time Sheet Detail",
+ "field_map": {
+ "name": "operation_id",
+ "operation": "operation",
+ "workstation": "workstation"
+ },
+ "postprocess": update_item,
+ "condition": lambda doc: doc.status != 'Completed'
+ }
+ }, target_doc, postprocess)
+
+ return doc
diff --git a/erpnext/manufacturing/doctype/production_order/test_production_order.py b/erpnext/manufacturing/doctype/production_order/test_production_order.py
index 1879e78e57..6ebb8e75d3 100644
--- a/erpnext/manufacturing/doctype/production_order/test_production_order.py
+++ b/erpnext/manufacturing/doctype/production_order/test_production_order.py
@@ -10,7 +10,6 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_per
from erpnext.manufacturing.doctype.production_order.production_order \
import make_stock_entry, ItemHasVariantError
from erpnext.stock.doctype.stock_entry import test_stock_entry
-from erpnext.projects.doctype.time_log.time_log import OverProductionLoggedError
from erpnext.stock.utils import get_bin
class TestProductionOrder(unittest.TestCase):
@@ -72,25 +71,25 @@ class TestProductionOrder(unittest.TestCase):
self.assertRaises(StockOverProductionError, s.submit)
- def test_make_time_log(self):
- from erpnext.projects.doctype.time_log.test_time_log import make_time_log_test_record
+ def test_make_time_sheet(self):
+ from erpnext.manufacturing.doctype.production_order.production_order import make_timesheet
prod_order = make_prod_order_test_record(item="_Test FG Item 2",
planned_start_date=now(), qty=1, do_not_save=True)
prod_order.set_production_order_operations()
prod_order.insert()
prod_order.submit()
-
+
d = prod_order.operations[0]
-
d.completed_qty = flt(d.completed_qty)
- time_log = make_time_log_test_record(hours=1, production_order= prod_order.name, operation= d.operation,
- completed_qty= prod_order.qty - d.completed_qty, operation_id=d.name, for_manufacturing=1, simulate=True)
+ name = frappe.db.get_value('Time Sheet', {'production_order': prod_order.name}, 'name')
+ time_sheet_doc = frappe.get_doc('Time Sheet', name)
+ time_sheet_doc.submit()
+
- self.assertEqual(prod_order.name, time_log.production_order)
- self.assertEqual((prod_order.qty - d.completed_qty), time_log.completed_qty)
- self.assertEqual(time_diff_in_hours(d.planned_end_time, d.planned_start_time),time_log.hours)
+ self.assertEqual(prod_order.name, time_sheet_doc.production_order)
+ self.assertEqual((prod_order.qty - d.completed_qty), sum([d.completed_qty for d in time_sheet_doc.timesheets]))
manufacturing_settings = frappe.get_doc({
"doctype": "Manufacturing Settings",
@@ -105,8 +104,11 @@ class TestProductionOrder(unittest.TestCase):
self.assertEqual(prod_order.operations[0].actual_operation_time, 60)
self.assertEqual(prod_order.operations[0].actual_operating_cost, 100)
+
+ time_sheet_doc1 = make_timesheet(prod_order.name)
+ self.assertEqual(len(time_sheet_doc1.get('timesheets')), 0)
- time_log.cancel()
+ time_sheet_doc.cancel()
prod_order.load_from_db()
self.assertEqual(prod_order.operations[0].status, "Pending")
@@ -115,11 +117,6 @@ class TestProductionOrder(unittest.TestCase):
self.assertEqual(flt(prod_order.operations[0].actual_operation_time), 0)
self.assertEqual(flt(prod_order.operations[0].actual_operating_cost), 0)
- time_log2 = make_time_log_test_record(from_time= add_days(time_log.to_time, 1) ,production_order= prod_order.name, operation= d.operation,
- completed_qty= 5, operation_id=d.name, for_manufacturing=1, do_not_save=True)
-
- self.assertRaises(OverProductionLoggedError, time_log2.save)
-
def test_planned_operating_cost(self):
prod_order = make_prod_order_test_record(item="_Test FG Item 2",
planned_start_date=now(), qty=1, do_not_save=True)
diff --git a/erpnext/manufacturing/doctype/production_order_operation/production_order_operation.json b/erpnext/manufacturing/doctype/production_order_operation/production_order_operation.json
index 923e534946..6542cc9645 100644
--- a/erpnext/manufacturing/doctype/production_order_operation/production_order_operation.json
+++ b/erpnext/manufacturing/doctype/production_order_operation/production_order_operation.json
@@ -2,6 +2,7 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2014-10-16 14:35:41.950175",
"custom": 0,
"docstatus": 0,
@@ -16,6 +17,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "",
@@ -24,6 +26,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -39,6 +42,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Operation",
@@ -50,6 +54,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -65,6 +70,7 @@
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Operation Description",
@@ -75,6 +81,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 1,
@@ -90,6 +97,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -97,6 +105,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -113,6 +122,7 @@
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Completed Qty",
@@ -121,6 +131,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -137,6 +148,7 @@
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Status",
@@ -146,6 +158,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -161,6 +174,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Workstation",
@@ -172,30 +186,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "depends_on": "eval:(doc.docstatus==1)",
- "fieldname": "show_time_logs",
- "fieldtype": "Button",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Show Time Logs",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -211,6 +202,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Estimated Time and Cost",
@@ -219,6 +211,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -234,6 +227,7 @@
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Planned Start Time",
@@ -242,6 +236,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -257,6 +252,7 @@
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Planned End Time",
@@ -265,6 +261,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -280,6 +277,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -287,6 +285,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -303,6 +302,7 @@
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Operation Time",
@@ -313,6 +313,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
@@ -328,6 +329,7 @@
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Hour Rate",
@@ -338,6 +340,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -353,6 +356,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Planned Operating Cost",
@@ -362,6 +366,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -377,6 +382,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Actual Time and Cost",
@@ -385,6 +391,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -400,6 +407,7 @@
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Actual Start Time",
@@ -408,6 +416,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -424,6 +433,7 @@
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Actual End Time",
@@ -432,6 +442,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -447,6 +458,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -454,6 +466,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -470,6 +483,7 @@
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Actual Operation Time",
@@ -478,6 +492,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -494,6 +509,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Actual Operating Cost",
@@ -503,55 +519,36 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
- },
- {
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "depends_on": "eval:(doc.docstatus==1 && doc.status!=\"Completed\")",
- "fieldname": "make_time_log",
- "fieldtype": "Button",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Make Time Log",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2015-11-16 06:29:52.476364",
+ "modified": "2016-06-24 16:12:08.380635",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Order Operation",
"name_case": "",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 65e755e112..0af9713b86 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -276,3 +276,8 @@ erpnext.patches.v7_0.re_route #2016-06-27
erpnext.patches.v7_0.create_warehouse_nestedset
erpnext.patches.v7_0.system_settings_setup_complete
erpnext.patches.v7_0.merge_account_type_stock_and_warehouse_to_stock
+erpnext.patches.v7_0.set_naming_series_for_timesheet
+erpnext.patches.v7_0.convert_timelogbatch_to_timesheet
+erpnext.patches.v7_0.convert_timelog_to_timesheet
+erpnext.patches.v7_0.move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet
+erpnext.patches.v7_0.remove_doctypes_and_reports
diff --git a/erpnext/patches/v5_0/rename_table_fieldnames.py b/erpnext/patches/v5_0/rename_table_fieldnames.py
index 37176f2609..d9af7b8402 100644
--- a/erpnext/patches/v5_0/rename_table_fieldnames.py
+++ b/erpnext/patches/v5_0/rename_table_fieldnames.py
@@ -182,8 +182,8 @@ rename_map = {
"Territory": [
["target_details", "targets"]
],
- "Time Log Batch": [
- ["time_log_batch_details", "time_logs"]
+ "Time Sheet": [
+ ["time_sheet_details", "time_logs"]
],
"Workstation": [
["workstation_operation_hours", "working_hours"]
diff --git a/erpnext/patches/v7_0/convert_timelog_to_timesheet.py b/erpnext/patches/v7_0/convert_timelog_to_timesheet.py
new file mode 100644
index 0000000000..9a77147cb4
--- /dev/null
+++ b/erpnext/patches/v7_0/convert_timelog_to_timesheet.py
@@ -0,0 +1,26 @@
+import frappe
+
+from erpnext.manufacturing.doctype.production_order.production_order import make_time_sheet, add_timesheet_detail
+
+def execute():
+ for data in frappe.get_all('Time Log', fields=["*"],
+ filters = [["docstatus", "<", "2"]]):
+ time_sheet = make_time_sheet(data.production_order)
+ args = get_timesheet_data(data)
+ add_timesheet_detail(time_sheet, args)
+ time_sheet.docstatus = data.docstatus
+ time_sheet.company = frappe.db.get_single_value('Global Defaults', 'default_company')
+ time_sheet.save(ignore_permissions=True)
+
+def get_timesheet_data(data):
+ return {
+ 'from_time': data.from_time,
+ 'hours': data.hours,
+ 'to_time': data.to_time,
+ 'project': data.project,
+ 'activity_type': data.activity_type or "Planning",
+ 'operation': data.operation,
+ 'operation_id': data.operation_id,
+ 'workstation': data.workstation,
+ 'completed_qty': data.completed_qty
+ }
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py b/erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py
new file mode 100644
index 0000000000..caacfe1dff
--- /dev/null
+++ b/erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py
@@ -0,0 +1,32 @@
+import frappe
+from frappe.utils import cint
+from erpnext.manufacturing.doctype.production_order.production_order import add_timesheet_detail
+
+def execute():
+ for tlb in frappe.get_all('Time Log Batch', fields=["*"],
+ filters = [["docstatus", "<", "2"]]):
+ time_sheet = frappe.new_doc('Time Sheet')
+ time_sheet.employee= ""
+ time_sheet.company = frappe.db.get_single_value('Global Defaults', 'default_company')
+ time_sheet.sales_invoice = tlb.sales_invoice
+ for data in tlb.time_logs:
+ args = get_timesheet_data(data)
+ add_timesheet_detail(time_sheet, args)
+
+ time_sheet.docstatus = tlb.docstatus
+ time_sheet.save(ignore_permissions=True)
+
+def get_timesheet_data(data):
+ time_log = frappe.get_doc('Time Log', data.time_log)
+
+ return {
+ 'from_time': time_log.from_time,
+ 'hours': time_log.hours,
+ 'to_time': time_log.to_time,
+ 'project': time_log.project,
+ 'activity_type': time_log.activity_type,
+ 'operation': time_log.operation,
+ 'operation_id': time_log.operation_id,
+ 'workstation': time_log.workstation,
+ 'completed_qty': time_log.completed_qty
+ }
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py b/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py
new file mode 100644
index 0000000000..55b51b7a59
--- /dev/null
+++ b/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py
@@ -0,0 +1,16 @@
+import frappe
+
+from erpnext.manufacturing.doctype.production_order.production_order import add_timesheet_detail
+
+def execute():
+ for si in frappe.db.sql(""" select sales_invoice as name from `tabTime Sheet`
+ where sales_invoice is not null and docstatus < 2""", as_dict=True):
+ si_doc = frappe.get_doc('Sales Invoice', si.name)
+ for item in si_doc.items:
+ if item.time_log_batch:
+ ts = si_doc.append('timesheets',{})
+ ts.time_sheet = item.time_log_batch
+ ts.billing_amount = frappe.db.get_value('Time Log Batch', item.time_log_batch, 'total_billing_amount')
+ si_doc.ignore_validate_update_after_submit = True
+ si_doc.update_time_sheet(ts.time_sheet)
+ si_doc.save()
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/remove_doctypes_and_reports.py b/erpnext/patches/v7_0/remove_doctypes_and_reports.py
new file mode 100644
index 0000000000..978c363562
--- /dev/null
+++ b/erpnext/patches/v7_0/remove_doctypes_and_reports.py
@@ -0,0 +1,9 @@
+import frappe
+
+def execute():
+ for doctype in ['Time Log Batch', 'Time Log Batch Detail', 'Time Log']:
+ frappe.delete_doc('DocType', doctype)
+
+ report = "Daily Time Log Summary"
+ if frappe.db.exists("Report", report):
+ frappe.delete_doc('Report', report)
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/set_naming_series_for_timesheet.py b/erpnext/patches/v7_0/set_naming_series_for_timesheet.py
new file mode 100644
index 0000000000..074621391e
--- /dev/null
+++ b/erpnext/patches/v7_0/set_naming_series_for_timesheet.py
@@ -0,0 +1,14 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from frappe.custom.doctype.property_setter.property_setter import make_property_setter
+
+def execute():
+ frappe.reload_doctype('Time Sheet')
+ frappe.reload_doctype('Time Sheet Detail')
+
+ make_property_setter('Time Sheet', "naming_series", "options", 'TS-', "Text")
+ make_property_setter('Time Sheet', "naming_series", "default", 'TS-', "Text")
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index b3f63b700e..5cef75c65c 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -67,7 +67,7 @@ frappe.ui.form.on("Project", {
},
show_dashboard: function(frm) {
frm.dashboard.show_heatmap = true;
- frm.dashboard.heatmap_message = __('This is based on the Time Logs created against this project');
+ frm.dashboard.heatmap_message = __('This is based on the Time Sheet created against this project');
frm.dashboard.show_dashboard();
if(frm.doc.__onload.activity_summary.length) {
@@ -82,9 +82,9 @@ frappe.ui.form.on("Project", {
sum: sum
}));
- section.on('click', '.time-log-link', function() {
+ section.on('click', '.time-sheet-link', function() {
var activity_type = $(this).attr('data-activity_type');
- frappe.set_route('List', 'Time Log',
+ frappe.set_route('List', 'Time Sheet',
{'activity_type': activity_type, 'project': frm.doc.name});
});
}
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index dbe2eb3502..ccc0cfe3ab 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -28,7 +28,7 @@ class Project(Document):
self.set_onload('links', self.meta.get_links_setup())
self.set_onload('activity_summary', frappe.db.sql('''select activity_type, sum(hours) as total_hours
- from `tabTime Log` where project=%s group by activity_type order by total_hours desc''', self.name, as_dict=True))
+ from `tabTime Sheet Detail` where project=%s group by activity_type order by total_hours desc''', self.name, as_dict=True))
def __setup__(self):
self.onload()
@@ -95,13 +95,13 @@ class Project(Document):
self.percent_complete = flt(flt(completed) / total * 100, 2)
def update_costing(self):
- from_time_log = frappe.db.sql("""select
+ from_time_sheet = frappe.db.sql("""select
sum(costing_amount) as costing_amount,
sum(billing_amount) as billing_amount,
min(from_time) as start_date,
max(to_time) as end_date,
sum(hours) as time
- from `tabTime Log` where project = %s and docstatus = 1""", self.name, as_dict=1)[0]
+ from `tabTime Sheet Detail` where project = %s and docstatus = 1""", self.name, as_dict=1)[0]
from_expense_claim = frappe.db.sql("""select
sum(total_sanctioned_amount) as total_sanctioned_amount
@@ -109,12 +109,12 @@ class Project(Document):
and docstatus = 1""",
self.name, as_dict=1)[0]
- self.actual_start_date = from_time_log.start_date
- self.actual_end_date = from_time_log.end_date
+ self.actual_start_date = from_time_sheet.start_date
+ self.actual_end_date = from_time_sheet.end_date
- self.total_costing_amount = from_time_log.costing_amount
- self.total_billing_amount = from_time_log.billing_amount
- self.actual_time = from_time_log.time
+ self.total_costing_amount = from_time_sheet.costing_amount
+ self.total_billing_amount = from_time_sheet.billing_amount
+ self.actual_time = from_time_sheet.time
self.total_expense_claim = from_expense_claim.total_sanctioned_amount
@@ -162,7 +162,7 @@ def get_dashboard_data(name):
def get_timeline_data(name):
'''Return timeline for attendance'''
return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*)
- from `tabTime Log` where project=%s
+ from `tabTime Sheet Detail` where project=%s
and from_time > date_sub(curdate(), interval 1 year)
and docstatus < 2
group by date(from_time)''', name))
diff --git a/erpnext/projects/doctype/project/project_dashboard.html b/erpnext/projects/doctype/project/project_dashboard.html
index 34a2d04514..f5bfbb7ca1 100644
--- a/erpnext/projects/doctype/project/project_dashboard.html
+++ b/erpnext/projects/doctype/project/project_dashboard.html
@@ -3,7 +3,7 @@
{% for d in data %}