diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 4e00c32808..637ecda624 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -62,11 +62,13 @@ class JournalEntry(AccountsController):
def on_cancel(self):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
+ from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
unlink_ref_doc_from_payment_entries(self.doctype, self.name)
-
+ unlink_ref_doc_from_salary_slip(self.name)
self.make_gl_entries(1)
self.update_advance_paid()
self.update_expense_claim()
+
def validate_party(self):
for d in self.get("accounts"):
diff --git a/erpnext/accounts/doctype/salary_component_account/salary_component_account.json b/erpnext/accounts/doctype/salary_component_account/salary_component_account.json
index 6bbde2247e..6ba820eff2 100644
--- a/erpnext/accounts/doctype/salary_component_account/salary_component_account.json
+++ b/erpnext/accounts/doctype/salary_component_account/salary_component_account.json
@@ -14,6 +14,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@@ -40,7 +41,8 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "description": "Default Bank / Cash account will be automatically updated in POS Invoice when this mode is selected.",
+ "columns": 0,
+ "description": "Default Bank / Cash account will be automatically updated in Salary Journal Entry when this mode is selected.",
"fieldname": "default_account",
"fieldtype": "Link",
"hidden": 0,
@@ -74,7 +76,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2016-07-27 17:24:24.956896",
+ "modified": "2016-09-02 07:49:06.567389",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Salary Component Account",
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index eefdc1dfd9..f3dc7bac56 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -701,4 +701,4 @@ def create_payment_gateway_account(gateway):
except frappe.DuplicateEntryError:
# already exists, due to a reinstall?
- pass
+ pass
\ No newline at end of file
diff --git a/erpnext/docs/assets/img/human-resources/bank-entry.png b/erpnext/docs/assets/img/human-resources/bank-entry.png
new file mode 100644
index 0000000000..b870bc5d49
Binary files /dev/null and b/erpnext/docs/assets/img/human-resources/bank-entry.png differ
diff --git a/erpnext/docs/assets/img/human-resources/process-payroll.png b/erpnext/docs/assets/img/human-resources/process-payroll.png
index ebbd9ae23c..2e2fbed923 100644
Binary files a/erpnext/docs/assets/img/human-resources/process-payroll.png and b/erpnext/docs/assets/img/human-resources/process-payroll.png differ
diff --git a/erpnext/docs/assets/img/human-resources/salary-structure-account.png b/erpnext/docs/assets/img/human-resources/salary-structure-account.png
new file mode 100644
index 0000000000..bd37ee0fbb
Binary files /dev/null and b/erpnext/docs/assets/img/human-resources/salary-structure-account.png differ
diff --git a/erpnext/docs/assets/img/human-resources/salary-structure.png b/erpnext/docs/assets/img/human-resources/salary-structure.png
index a135092975..71f6013f94 100644
Binary files a/erpnext/docs/assets/img/human-resources/salary-structure.png and b/erpnext/docs/assets/img/human-resources/salary-structure.png differ
diff --git a/erpnext/docs/user/manual/en/human-resources/salary-and-payroll.md b/erpnext/docs/user/manual/en/human-resources/salary-and-payroll.md
index 62933df029..286df1400b 100644
--- a/erpnext/docs/user/manual/en/human-resources/salary-and-payroll.md
+++ b/erpnext/docs/user/manual/en/human-resources/salary-and-payroll.md
@@ -81,6 +81,12 @@ In the “Earnings” and “Deductions” tables, you can calculate the values
* Only Formula
* Only Amount
+#### Figure 1.5:Account Details
+
+
+
+ * Select Mode of Payment and Payment Account for the Salary Slips which will be generated using this Salary Structure
+
Save the Salary Structure.
In conditions and formulas,
@@ -128,8 +134,10 @@ You can also create salary slip for multiple employees using Process Payroll:
In Process Payroll,
1. Select the Company for which you want to create the Salary Slips.
- 2. Select the Month and the Year for which you want to create the Salary Slips.
- 3. Click on “Create Salary Slips”. This will create Salary Slip records for each active Employee for the month selected. If the Salary Slips are created, the system will not create any more Salary Slips. All updates will be shown in the “Activity Log” section.
+ 2. Check "Salary Slip based on Timesheet" if you want to process timesheet based Salary Slips.
+ 3. Select the From Date and To Date or Fiscal year and month for which you want to create the Salary Slips.
+ 3. Select the Payment Account.
+ 3. Click on “Create Salary Slips”. This will create Salary Slip records for each active Employee for the time period selected. If the Salary Slips are created, the system will not create any more Salary Slips. All updates will be shown in the “Activity Log” section.
4. Once all Salary Slips are created, you can check if they are created correctly or edit it if you want to deduct Leave Without Pay (LWP).
5. After checking, you can “Submit” them all together by clicking on “Submit Salary Slips”. 1. If you want them to be automatically emailed to the Employee, make sure to check the “Send Email” box.
@@ -144,12 +152,19 @@ there is only one payment entry in the company’s books of accounts and anyone
with access to the company’s accounts will not have access to the individual
salaries.
-The salary payment entry is a Journal Entry entry that debits the total
-salary of all Employees to the Salary Account and credits the company’s bank
-Account.
+The salary payment entry is a Journal Entry that debits the total of the
+earning type salary component and credits the total of deduction type salary
+component of all Employees to the default account set at Salary Component level
+for each component.
To generate your salary payment voucher from Process Payroll, click on
-“Make Bank Voucher” and a new Journal Entry with the total salaries will be
+“Make Bank Entry”. It will ask to enter the Bank Transaction Reference Number and date.
+Click on "Make" and a new Journal Entry with the total salary components will be
created.
+#### Figure 3.1: Make Bank Entry
+
+
+
+
{next}
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.js b/erpnext/hr/doctype/process_payroll/process_payroll.js
index 1c60a5fc1d..6c057e1701 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.js
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.js
@@ -1,6 +1,62 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
+frappe.ui.form.on("Process Payroll", {
+ refresh: function(frm) {
+ frm.disable_save();
+ frm.trigger("toggle_fields");
+ frm.trigger("set_month_dates");
+ },
+
+ month: function(frm) {
+ frm.trigger("set_month_dates");
+ },
+
+ fiscal_year: function(frm) {
+ frm.trigger("set_month_dates");
+ },
+
+ salary_slip_based_on_timesheet: function(frm) {
+ frm.trigger("toggle_fields")
+ },
+
+ toggle_fields: function(frm) {
+ frm.toggle_display(['from_date','to_date'],
+ cint(frm.doc.salary_slip_based_on_timesheet)==1);
+ frm.toggle_display(['fiscal_year', 'month'],
+ cint(frm.doc.salary_slip_based_on_timesheet)==0);
+ },
+
+ set_month_dates: function(frm) {
+ if (!frm.doc.salary_slip_based_on_timesheet){
+ frappe.call({
+ method:'erpnext.hr.doctype.process_payroll.process_payroll.get_month_details',
+ args:{
+ year: frm.doc.fiscal_year,
+ month: frm.doc.month
+ },
+ callback: function(r){
+ if (r.message){
+ frm.set_value('from_date', r.message.month_start_date);
+ frm.set_value('to_date', r.message.month_end_date);
+ }
+ }
+ })
+ }
+ }
+})
+
+cur_frm.cscript.onload = function(doc,cdt,cdn){
+ if(!doc.month) {
+ var today=new Date();
+ month = (today.getMonth()+01).toString();
+ if(month.length>1) doc.month = month;
+ else doc.month = '0'+month;
+ }
+ if(!doc.fiscal_year) doc.fiscal_year = sys_defaults['fiscal_year'];
+ refresh_many(['month', 'fiscal_year']);
+}
+
cur_frm.cscript.display_activity_log = function(msg) {
if(!cur_frm.ss_html)
cur_frm.ss_html = $a(cur_frm.fields_dict['activity_log'].wrapper,'div');
@@ -26,7 +82,7 @@ cur_frm.cscript.create_salary_slip = function(doc, cdt, cdn) {
cur_frm.cscript.submit_salary_slip = function(doc, cdt, cdn) {
cur_frm.cscript.display_activity_log("");
- frappe.confirm(__("Do you really want to Submit all Salary Slip for month {0} and year {1}", [doc.month, doc.fiscal_year]), function() {
+ frappe.confirm(__("Do you really want to Submit all Salary Slip for Account {0} from {1} to {2}", [doc.payment_account, doc.from_date, doc.to_date]), function() {
// clear all in locals
if(locals["Salary Slip"]) {
$.each(locals["Salary Slip"], function(name, d) {
@@ -44,22 +100,45 @@ cur_frm.cscript.submit_salary_slip = function(doc, cdt, cdn) {
}
cur_frm.cscript.make_bank_entry = function(doc,cdt,cdn){
- if(doc.company && doc.month && doc.fiscal_year){
- cur_frm.cscript.make_jv(doc, cdt, cdn);
+ if(doc.company && doc.from_date && doc.to_date){
+ return cur_frm.cscript.reference_entry(doc,cdt,cdn);
} else {
- msgprint(__("Company, Month and Fiscal Year is mandatory"));
+ msgprint(__("Company, From Date and To Date is mandatory"));
}
}
-cur_frm.cscript.make_jv = function(doc, dt, dn) {
- return $c_obj(doc, 'make_journal_entry', '', function(r) {
- var doc = frappe.model.sync(r.message)[0];
- frappe.set_route("Form", doc.doctype, doc.name);
+cur_frm.cscript.reference_entry = function(doc,cdt,cdn){
+ var dialog = new frappe.ui.Dialog({
+ title: __("Bank Transaction Reference"),
+ fields: [
+ {
+ "label": __("Reference Number"),
+ "fieldname": "reference_number",
+ "fieldtype": "Data",
+ "reqd": 1
+ },
+ {
+ "label": __("Reference Date"),
+ "fieldname": "reference_date",
+ "fieldtype": "Date",
+ "reqd": 1,
+ "default": get_today()
+ }
+ ]
});
-}
-
-frappe.ui.form.on("Process Payroll", {
- refresh: function(frm) {
- frm.disable_save();
- }
-})
+ dialog.set_primary_action(__("Make"), function() {
+ args = dialog.get_values();
+ if(!args) return;
+ dialog.hide();
+ return frappe.call({
+ doc: cur_frm.doc,
+ method: "make_journal_entry",
+ args: {"reference_number": args.reference_number, "reference_date":args.reference_date},
+ callback: function(r) {
+ if (r.message)
+ cur_frm.cscript.display_activity_log(r.message);
+ }
+ });
+ });
+ dialog.show();
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.json b/erpnext/hr/doctype/process_payroll/process_payroll.json
index 8d8124b864..13d3191181 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.json
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.json
@@ -8,11 +8,13 @@
"docstatus": 0,
"doctype": "DocType",
"document_type": "Other",
+ "editable_grid": 0,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "section_break0",
"fieldtype": "Section Break",
"hidden": 0,
@@ -37,6 +39,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break0",
"fieldtype": "Column Break",
"hidden": 0,
@@ -61,6 +64,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@@ -86,6 +90,59 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
+ "default": "Today",
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Posting 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": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 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,
+ "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,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "branch",
"fieldtype": "Link",
"hidden": 0,
@@ -111,30 +168,7 @@
"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,
- "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,
- "collapsible": 0,
+ "columns": 0,
"fieldname": "department",
"fieldtype": "Link",
"hidden": 0,
@@ -160,6 +194,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "designation",
"fieldtype": "Link",
"hidden": 0,
@@ -185,6 +220,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "section_break_8",
"fieldtype": "Section Break",
"hidden": 0,
@@ -209,9 +245,10 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "salary_slip_based_on_timesheet",
"fieldtype": "Check",
- "hidden": 1,
+ "hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
@@ -234,14 +271,15 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "select_payroll_year_and_month",
+ "columns": 0,
+ "fieldname": "select_payroll_period",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Select Payroll Year and Month",
+ "label": "Select Payroll Period",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -259,6 +297,33 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
+ "fieldname": "from_date",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "From",
+ "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,
+ "columns": 0,
"fieldname": "fiscal_year",
"fieldtype": "Link",
"hidden": 0,
@@ -284,6 +349,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"hidden": 0,
@@ -308,6 +374,33 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
+ "fieldname": "to_date",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "To",
+ "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,
+ "columns": 0,
"fieldname": "month",
"fieldtype": "Select",
"hidden": 0,
@@ -333,32 +426,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "default": "Today",
- "fieldname": "posting_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Posting 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": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
+ "columns": 0,
"fieldname": "process_payroll",
"fieldtype": "Section Break",
"hidden": 0,
@@ -384,6 +452,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break2",
"fieldtype": "Column Break",
"hidden": 0,
@@ -408,6 +477,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"description": "Creates salary slip for above mentioned criteria.",
"fieldname": "create_salary_slip",
"fieldtype": "Button",
@@ -433,6 +503,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break3",
"fieldtype": "Column Break",
"hidden": 0,
@@ -457,6 +528,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"description": "Submit all salary slips for the above selected criteria",
"fieldname": "submit_salary_slip",
"fieldtype": "Button",
@@ -482,16 +554,19 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "column_break4",
- "fieldtype": "Column Break",
+ "columns": 0,
+ "fieldname": "account",
+ "fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "label": "Account",
"length": 0,
"no_copy": 0,
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -499,13 +574,42 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "unique": 0,
- "width": "25%"
+ "unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
+ "description": "Select Payment Account to make Bank Entry",
+ "fieldname": "payment_account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Payment Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "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,
+ "columns": 0,
+ "depends_on": "payment_account",
"description": "Create Bank Entry for the total salary paid for the above selected criteria",
"fieldname": "make_bank_entry",
"fieldtype": "Button",
@@ -531,6 +635,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "section_break2",
"fieldtype": "Section Break",
"hidden": 0,
@@ -554,6 +659,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "activity_log",
"fieldtype": "HTML",
"hidden": 0,
@@ -586,7 +692,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-09-19 15:12:54.090381",
+ "modified": "2016-09-28 05:43:26.472928",
"modified_by": "Administrator",
"module": "HR",
"name": "Process Payroll",
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.py b/erpnext/hr/doctype/process_payroll/process_payroll.py
index 3e6e3e0251..f31ddfb26b 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.py
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.py
@@ -5,12 +5,13 @@ from __future__ import unicode_literals
import frappe
from frappe.utils import cint, flt, nowdate
from frappe import _
+import collections
+from collections import defaultdict
from frappe.model.document import Document
class ProcessPayroll(Document):
-
def get_emp_list(self):
"""
Returns list of active employees based on selected criteria
@@ -21,19 +22,20 @@ class ProcessPayroll(Document):
sal_struct = frappe.db.sql("""
select name from `tabSalary Structure`
- where docstatus != 2 and
- ifnull(salary_slip_based_on_timesheet,0) = 0""")
-
+ where docstatus != 2 and payment_account = '%(payment_account)s' and
+ ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s"""%
+ {"payment_account": self.payment_account, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet})
+
if sal_struct:
cond += "and t2.parent IN %(sal_struct)s "
- emp_list = frappe.db.sql("""
- select t1.name
- from `tabEmployee` t1, `tabSalary Structure Employee` t2
- where t1.docstatus!=2 and t1.name = t2.employee
- %s """% cond, {"sal_struct": sal_struct})
+ emp_list = frappe.db.sql("""
+ select t1.name
+ from `tabEmployee` t1, `tabSalary Structure Employee` t2
+ where t1.docstatus!=2 and t1.name = t2.employee
+ %s """% cond, {"sal_struct": sal_struct})
- return emp_list
+ return emp_list
def get_filter_condition(self):
@@ -48,16 +50,15 @@ class ProcessPayroll(Document):
def get_joining_releiving_condition(self):
- m = get_month_details(self.fiscal_year, self.month)
cond = """
- and ifnull(t1.date_of_joining, '0000-00-00') <= '%(month_end_date)s'
- and ifnull(t1.relieving_date, '2199-12-31') >= '%(month_start_date)s'
- """ % m
+ and ifnull(t1.date_of_joining, '0000-00-00') <= '%(from_date)s'
+ and ifnull(t1.relieving_date, '2199-12-31') >= '%(to_date)s'
+ """ % {"from_date": self.from_date, "to_date": self.to_date}
return cond
def check_mandatory(self):
- for f in ['company', 'month', 'fiscal_year']:
+ for f in ['company', 'from_date', 'to_date']:
if not self.get(f):
frappe.throw(_("Please set {0}").format(f))
@@ -65,27 +66,37 @@ class ProcessPayroll(Document):
"""
Creates salary slip for selected employees if already not created
"""
-
self.check_permission('write')
emp_list = self.get_emp_list()
ss_list = []
- for emp in emp_list:
- if not frappe.db.sql("""select name from `tabSalary Slip`
- where docstatus!= 2 and employee = %s and month = %s and fiscal_year = %s and company = %s
- """, (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,
- "company": self.company,
- "posting_date": self.posting_date,
- })
- ss.insert()
- ss_list.append(ss.name)
-
+ if emp_list:
+ for emp in emp_list:
+ if not frappe.db.sql("""select name from `tabSalary Slip`
+ where docstatus!= 2 and employee = %s and start_date >= %s and end_date <= %s and company = %s
+ """, (emp[0], self.from_date, self.to_date, self.company)):
+ if self.salary_slip_based_on_timesheet:
+ ss = frappe.get_doc({
+ "doctype": "Salary Slip",
+ "salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet,
+ "start_date": self.from_date,
+ "end_date": self.to_date,
+ "employee": emp[0],
+ "company": self.company,
+ "posting_date": self.posting_date
+ })
+ else:
+ ss = frappe.get_doc({
+ "doctype": "Salary Slip",
+ "salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet,
+ "fiscal_year": self.fiscal_year,
+ "month": self.month,
+ "employee": emp[0],
+ "company": self.company,
+ "posting_date": self.posting_date
+ })
+ ss.insert()
+ ss_list.append(ss.name)
return self.create_log(ss_list)
@@ -97,16 +108,17 @@ class ProcessPayroll(Document):
return log
- def get_sal_slip_list(self):
+ def get_sal_slip_list(self, ss_status, as_dict=False):
"""
Returns list of salary slips based on selected criteria
- which are not submitted
"""
cond = self.get_filter_condition()
+
ss_list = frappe.db.sql("""
- select t1.name from `tabSalary Slip` t1
- where t1.docstatus = 0 and month = %s and fiscal_year = %s %s
- """ % ('%s', '%s', cond), (self.month, self.fiscal_year))
+ select t1.name, t1.salary_structure from `tabSalary Slip` t1
+ where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s
+ and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s %s
+ """ % ('%s', '%s', '%s','%s', cond), (ss_status, self.from_date, self.to_date, self.salary_slip_based_on_timesheet), as_dict=as_dict)
return ss_list
@@ -116,16 +128,17 @@ class ProcessPayroll(Document):
"""
self.check_permission('write')
- ss_list = self.get_sal_slip_list()
+ ss_list = self.get_sal_slip_list(ss_status=0)
not_submitted_ss = []
for ss in ss_list:
ss_obj = frappe.get_doc("Salary Slip",ss[0])
- try:
- ss_obj.submit()
- except Exception,e:
+ if ss_obj.net_pay<0:
not_submitted_ss.append(ss[0])
- frappe.msgprint(e)
- continue
+ else:
+ try:
+ ss_obj.submit()
+ except Exception,e:
+ not_submitted_ss.append(ss[0])
return self.create_submit_log(ss_list, not_submitted_ss)
@@ -148,6 +161,7 @@ class ProcessPayroll(Document):
Not Submitted Salary Slips: \
%s
\
Reason:
\
+ May be net pay is less than 0
May be company email id specified in employee master is not valid.
\
Please mention correct email id in employee master or if you don't want to \
send mail, uncheck 'Send Email' checkbox.
\
@@ -166,38 +180,105 @@ class ProcessPayroll(Document):
cond = self.get_filter_condition()
tot = frappe.db.sql("""
select sum(rounded_total) from `tabSalary Slip` t1
- where t1.docstatus = 1 and month = %s and fiscal_year = %s %s
- """ % ('%s', '%s', cond), (self.month, self.fiscal_year))
+ where t1.docstatus = 1 and start_date >= %s and end_date <= %s %s
+ """ % ('%s', '%s', cond), (self.from_date, self.to_date))
return flt(tot[0][0])
-
-
- def make_journal_entry(self, salary_account = None):
+
+ def get_salary_component_account(self, salary_component):
+ account = frappe.db.get_value("Salary Component Account",
+ {"parent": salary_component, "company": self.company}, "default_account")
+
+ if not account:
+ frappe.throw(_("Please set default account in Salary Component {0}")
+ .format(salary_component))
+
+ return account
+
+ def get_salary_components(self, component_type):
+ salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True)
+ if salary_slips:
+ salary_components = frappe.db.sql("""select salary_component, amount, parentfield
+ from `tabSalary Detail` where parentfield = '%s' and parent in (%s)""" %
+ (component_type, ', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=True)
+ return salary_components
+
+ def get_salary_component_total(self, component_type = None):
+ salary_components = self.get_salary_components(component_type)
+ if salary_components:
+ component_dict = {}
+ for item in salary_components:
+ component_dict[item['salary_component']] = component_dict.get(item['salary_component'], 0) + item['amount']
+ account_details = self.get_account(component_dict = component_dict)
+ return account_details
+
+ def get_account(self, component_dict = None):
+ account_dict = {}
+ for s, a in component_dict.items():
+ account = self.get_salary_component_account(s)
+ account_dict[account] = account_dict.get(account, 0) + a
+ return account_dict
+
+
+ def make_journal_entry(self, reference_number = None, reference_date = None):
self.check_permission('write')
+ earnings = self.get_salary_component_total(component_type = "earnings")
+ deductions = self.get_salary_component_total(component_type = "deductions")
+ jv_name = ""
- amount = self.get_total_salary()
- default_bank_account = frappe.db.get_value("Company", self.company,
- "default_bank_account")
+ if earnings or deductions:
+ journal_entry = frappe.new_doc('Journal Entry')
+ journal_entry.voucher_type = 'Bank Entry'
+ journal_entry.user_remark = _('Payment of salary from {0} to {1}').format(self.from_date,
+ self.to_date)
+ journal_entry.company = self.company
+ journal_entry.posting_date = nowdate()
+
+ account_amt_list = []
+ adjustment_amt = 0
+ for acc, amt in earnings.items():
+ adjustment_amt = adjustment_amt+amt
+ account_amt_list.append({
+ "account": acc,
+ "debit_in_account_currency": amt
+ })
+ for acc, amt in deductions.items():
+ adjustment_amt = adjustment_amt-amt
+ account_amt_list.append({
+ "account": acc,
+ "credit_in_account_currency": amt
+ })
+ account_amt_list.append({
+ "account": self.payment_account,
+ "credit_in_account_currency": adjustment_amt
+ })
+ journal_entry.set("accounts", account_amt_list)
+ journal_entry.cheque_no = reference_number
+ journal_entry.cheque_date = reference_date
+ journal_entry.save()
+ try:
+ journal_entry.submit()
+ jv_name = journal_entry.name
+ self.update_salary_slip_status(jv_name = jv_name)
+ except Exception, e:
+ frappe.msgprint(e)
+ return self.create_jv_log(jv_name)
+
- journal_entry = frappe.new_doc('Journal Entry')
- journal_entry.voucher_type = 'Bank Entry'
- journal_entry.user_remark = _('Payment of salary for the month {0} and year {1}').format(self.month,
- self.fiscal_year)
- journal_entry.fiscal_year = self.fiscal_year
- journal_entry.company = self.company
- journal_entry.posting_date = nowdate()
- journal_entry.set("accounts", [
- {
- "account": salary_account,
- "debit_in_account_currency": amount
- },
- {
- "account": default_bank_account,
- "credit_in_account_currency": amount
- },
- ])
+ def create_jv_log(self, jv_name):
+ log = "
" + _("No submitted Salary Slip found") + "
" + if jv_name: + log = "" + _("Journal Entry Submitted") + "\ + %s" % '